遷移64位虛擬機(jī)未必性能更好
業(yè)務(wù)量上升以后,需要使用的內(nèi)存隨之增加,而在通常32位系統(tǒng)上,單個(gè)進(jìn)程占用的最大內(nèi)存通常是2GB,且考慮到堆外內(nèi)存的使用,32位機(jī)器可能無(wú)法滿足內(nèi)存要求,一種常見的應(yīng)對(duì)方式就是換用64位服務(wù)器。而對(duì)于Java,由于指針膨脹和字節(jié)對(duì)齊,同一個(gè)程序在64位虛擬機(jī)上占用的內(nèi)存會(huì)多于32位虛擬機(jī)。開發(fā)者換用64位虛擬機(jī)后,很可能會(huì)增加虛擬機(jī)的堆大小,而這將導(dǎo)致 Full GC 垃圾回收的時(shí)間大大增加,導(dǎo)出堆快照也變得困難。
因此,使用64位虛擬機(jī)增加內(nèi)存時(shí),需要特別注意對(duì)內(nèi)存的使用,盡量不要觸發(fā)Full
GC導(dǎo)致長(zhǎng)時(shí)間停頓。另一種方法是建立32位虛擬機(jī)的集群來(lái)提高性能。
堆外內(nèi)存溢出
Java本地方法調(diào)用,如調(diào)用C++實(shí)現(xiàn)的本地模塊、NIO的DirectByteBuffer,都會(huì)占用大量?jī)?nèi)存。而在開發(fā)中,開發(fā)者往往重點(diǎn)關(guān)注了堆內(nèi)存的大小,在內(nèi)存溢出時(shí)也傾向于增加堆內(nèi)存,而忽視了堆外內(nèi)存的使用。堆外內(nèi)存并不會(huì)像堆內(nèi)存一樣不足馬上通知GC進(jìn)行垃圾回收,堆外內(nèi)存只能等待老年代空間不足進(jìn)行Full
GC時(shí)順便回收內(nèi)存,否則堆外內(nèi)存只能等到空間不足時(shí)拋出內(nèi)存溢出異常,然后請(qǐng)求GC進(jìn)行回收。
因此,配置虛擬機(jī),除考慮常規(guī)的堆大小外,優(yōu)化時(shí)還需要考慮Direct
Memory、線程棧、socket緩沖區(qū)、JNI代碼、虛擬機(jī)、GC占用的內(nèi)存大小。
外部命令的時(shí)間消耗
Java開發(fā)中如果需要執(zhí)行shell腳本,可以使用Runtime.getRuntime().exec方法,還能從返回的Process對(duì)象中讀取標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出、等待執(zhí)行結(jié)束。根據(jù)方法注釋,該方法首先復(fù)制當(dāng)前進(jìn)程產(chǎn)生一個(gè)子進(jìn)程,在子進(jìn)程中執(zhí)行命令,結(jié)束后退出子進(jìn)程。
進(jìn)程的復(fù)制比較消耗CPU和內(nèi)存,應(yīng)盡量通過(guò)Java程序本身去完成相關(guān)功能。
多線程使用線程池
Java虛擬機(jī)中沒有給用戶用的多進(jìn)程方法,并行處理更多地使用多線程方式。默認(rèn)情況下,Linux限制用戶的線程數(shù)量上限為1024,當(dāng)然包括了系統(tǒng)中運(yùn)行的所有線程。通常情況下,線程資源不會(huì)被耗盡,但多線程程序如果頻繁創(chuàng)建新線程也會(huì)遇到線程資源不足的情況。一方面,可以調(diào)整系統(tǒng)設(shè)置,提高線程數(shù)上限,另一方面,應(yīng)盡量避免頻繁創(chuàng)建線程。線程雖小,創(chuàng)建時(shí)一樣要消耗時(shí)間和內(nèi)存。
多線程程序應(yīng)盡量采用Java的線程池,這樣線程的個(gè)數(shù)總體可控,使用時(shí)可以避免創(chuàng)建線程的時(shí)間消耗。Java提供了多種功能強(qiáng)大的線程池類型,基于線程池可以對(duì)任務(wù)進(jìn)行緩存、按照一定的時(shí)間頻率執(zhí)行任務(wù)、返回執(zhí)行結(jié)果、分叉與合并等。