如何在高性能服務(wù)器上進(jìn)行JVM調(diào)優(yōu)?
為了充分利用高性能服務(wù)器的硬件資源螺男,有兩種JVM調(diào)優(yōu)方案下隧,它們都有各自的優(yōu)缺點(diǎn)启涯,需要根據(jù)具體的情況進(jìn)行選擇黎做。
1昧互、采用64位操作系統(tǒng)挽铁,并為JVM分配大內(nèi)存
我們知道,如果JVM中堆內(nèi)存太小敞掘,那么就會(huì)頻繁地發(fā)生垃圾回收叽掘,而垃圾回收都會(huì)伴隨不同程度的程序停頓,因此玖雁,如果擴(kuò)大堆內(nèi)存的話可以減少垃圾回收的頻率更扁,從而避免程序的停頓。
因此,人們自然而然想到擴(kuò)大內(nèi)存容量浓镜。而32位操作系統(tǒng)理論上最大只支持4G內(nèi)存溃列,64位操作系統(tǒng)最大能支持128G內(nèi)存,因此我們可以使用64位操作系統(tǒng)膛薛,并使用64位JVM听隐,并為JVM分配更大的堆內(nèi)存。但問(wèn)題也隨之而來(lái)哄啄。
堆內(nèi)存變大后雅任,雖然垃圾收集的頻率減少了,但每次垃圾回收的時(shí)間變長(zhǎng)咨跌。如果對(duì)內(nèi)存為14G沪么,那么每次Full GC將長(zhǎng)達(dá)數(shù)十秒。如果Full GC頻繁發(fā)生锌半,那么對(duì)于一個(gè)網(wǎng)站來(lái)說(shuō)是無(wú)法忍受的禽车。
因此,對(duì)于使用大內(nèi)存的程序來(lái)說(shuō)刊殉,一定要減少Full GC的頻率哭当,如果每天只有一兩次Full GC,而且發(fā)生在半夜冗澈, 那完全可以接受钦勘。
要減少Full GC的頻率,就要盡量避免太多對(duì)象進(jìn)入老年代亚亲,可以有以下做法:
確保對(duì)象都是“朝生夕死”的?
一個(gè)對(duì)象使用完后應(yīng)盡快讓他失效彻采,然后盡快在新生代中被Minor GC回收掉,盡量避免對(duì)象在新生代中停留太長(zhǎng)時(shí)間捌归。
提高大對(duì)象直接進(jìn)入老年代的門(mén)檻?
通過(guò)設(shè)置參數(shù)-XX:PretrnureSizeThreshold來(lái)提高大對(duì)象的門(mén)檻肛响,盡量讓對(duì)象都先進(jìn)入新生代,然后盡快被Minor GC回收掉惜索,而不要直接進(jìn)入老年代特笋。?
注意:使用64位JDK的注意點(diǎn)
64位JDK支持更大的堆內(nèi)存,但更大的堆內(nèi)存會(huì)導(dǎo)致一次垃圾回收時(shí)間過(guò)長(zhǎng)巾兆。
現(xiàn)階段猎物,64位JDK的性能普遍比32位JDK低。
堆內(nèi)存過(guò)大無(wú)法在發(fā)生內(nèi)存溢出時(shí)生成內(nèi)存快照?
若將堆內(nèi)存設(shè)為10G角塑,那么當(dāng)堆內(nèi)存溢出時(shí)就要生成10G的大文件蔫磨,這基本上是不可能的。
相同程序圃伶,64位JDK要比32位JDK消耗更大的內(nèi)存?
2. 使用32位JVM集群
針對(duì)于64位JDK種種弊端堤如,我們更多選擇使用32位JDK集群來(lái)充分利用高性能機(jī)器的硬件資源蒲列。
如何實(shí)現(xiàn)?
在一臺(tái)服務(wù)器上運(yùn)行多個(gè)服務(wù)器程序搀罢,這些程序都運(yùn)行在32位的JDK上蝗岖。然后再運(yùn)行個(gè)服務(wù)器作為反向代理服務(wù)器,由它來(lái)實(shí)現(xiàn)負(fù)載均衡榔至。?
由于32位JDK最多支持2G內(nèi)存剪侮,因此每個(gè)虛擬結(jié)點(diǎn)的堆內(nèi)存可以分配1.6G,一共運(yùn)行10個(gè)虛擬結(jié)點(diǎn)的話洛退,這臺(tái)物理服務(wù)器可以擁有16G的堆內(nèi)存瓣俯。?
有啥弊端?
多個(gè)虛擬節(jié)點(diǎn)競(jìng)爭(zhēng)共享資源時(shí)容易出現(xiàn)問(wèn)題?
如多個(gè)虛擬節(jié)點(diǎn)共同競(jìng)爭(zhēng)IO操作兵怯,很可能會(huì)引起IO異常彩匕。
很難高效地使用資源池?
如果每個(gè)虛擬節(jié)點(diǎn)使用各自的資源池,那么無(wú)法實(shí)現(xiàn)各個(gè)資源池的負(fù)載均衡媒区。如果使用集中式資源池驼仪,那么又存在競(jìng)爭(zhēng)的問(wèn)題。
每個(gè)虛擬節(jié)點(diǎn)最大內(nèi)存為2G
別忘了直接內(nèi)存也可能導(dǎo)致內(nèi)存溢出袜漩!
問(wèn)題描述
有個(gè)小型網(wǎng)站绪爸,使用32位JDK,堆1.6G宙攻。運(yùn)行期間發(fā)現(xiàn)老是出現(xiàn)內(nèi)存溢出奠货。為了判斷是否是堆內(nèi)存溢出,在程序運(yùn)行前添加參數(shù):-XX:+HeapDumpOnOutOfMemeryError(添加這個(gè)參數(shù)后當(dāng)堆內(nèi)存溢出時(shí)就會(huì)輸出異常日至)座掘。但當(dāng)再次發(fā)生內(nèi)存溢出時(shí)递惋,沒(méi)有生成相關(guān)異常日志。從而可以判定溢陪,不是堆內(nèi)存發(fā)生溢出萍虽。?
問(wèn)題分析
我們可以發(fā)現(xiàn),在32位JDK中形真,將1.6G分配給了堆杉编,還有一部分分配給了JVM的其它內(nèi)存,只有少于0.4G的內(nèi)存為非JVM內(nèi)存咆霜。我們知道邓馒,如果使用了NIO,那么JVM會(huì)在JVM內(nèi)存之外分配內(nèi)存空間裕便,這部分內(nèi)存也叫“直接內(nèi)存”绒净。因此见咒,如果程序中使用了NIO偿衰,那么就要小心“直接內(nèi)存”不足時(shí)發(fā)生內(nèi)存溢出異常了!?
直接內(nèi)存的垃圾回收過(guò)程
直接內(nèi)存雖然不是JVM內(nèi)存空間,但它的垃圾回收也有JVM負(fù)責(zé)下翎。直接內(nèi)存的垃圾回收發(fā)生在Full GC時(shí)缤言,只有當(dāng)老年代內(nèi)存滿時(shí),垃圾收集器才會(huì)順便收集一下直接內(nèi)存中的垃圾视事。?
如果直接內(nèi)存已滿胆萧,但老年代沒(méi)滿,這時(shí)直接內(nèi)存先是拋出異常俐东,相應(yīng)的catch塊中調(diào)用System.gc()跌穗。由于System.gc()只是建議JVM回收,JVM可能不馬上回收內(nèi)存虏辫,那么這時(shí)直接內(nèi)存就拋出內(nèi)存溢出異常蚌吸,使得程序終止。
JVM崩潰的原因
當(dāng)內(nèi)存溢出時(shí)砌庄,JVM僅僅會(huì)終止當(dāng)前運(yùn)行的程序羹唠,那么什么時(shí)候JVM會(huì)崩潰呢??
什么是異步請(qǐng)求娄昆?
我們知道佩微,Web服務(wù)器和客戶端采用HTTP通信,而HTTP底層采用TCP通信萌焰。異步通信就是當(dāng)客戶端向服務(wù)器發(fā)送一個(gè)HTTP請(qǐng)求后哺眯,將這個(gè)請(qǐng)求的TCP連接委托給其它線程,然后它轉(zhuǎn)而做別的事扒俯,那條被委托的線程保持TCP連接族购,等待服務(wù)器的回信。當(dāng)收到服務(wù)器回信后陵珍,再將收到的數(shù)據(jù)轉(zhuǎn)交給剛才的線程寝杖。這個(gè)過(guò)程就是異步通信過(guò)程。?
異步請(qǐng)求如何造成JVM崩潰互纯?
如果一個(gè)Web應(yīng)用使用了較多的異步請(qǐng)求(AJAX)瑟幕,每次主線程發(fā)送完請(qǐng)求后都將TCP連接交給一條新的線程去等待服務(wù)器回信,那么如果網(wǎng)絡(luò)不流暢時(shí)留潦,這些受委托的線程遲遲等不到服務(wù)器的回信只盹,因此保持著TCP連接。當(dāng)TCP連接過(guò)多時(shí),超過(guò)JVM的承受能力,JVM就發(fā)生崩潰放仗。
如何處理大對(duì)象奕删?
大對(duì)象對(duì)于JVM來(lái)說(shuō)是個(gè)噩耗。如果對(duì)象過(guò)大嗡善,當(dāng)前新生代的剩余空間裝不下它岗宣,那么就需要使用分配擔(dān)保機(jī)制热某,將當(dāng)前新生代的對(duì)象都復(fù)制到老年代中菩鲜,給大對(duì)象騰出空間园细。分配擔(dān)保涉及到大量的復(fù)制,因此效率很低接校。
那么猛频,如果將大對(duì)象直接放入老年代,雖然避免了分配擔(dān)保過(guò)程蛛勉,但該對(duì)象只有當(dāng)Full GC時(shí)才能被回收鹿寻,而Full GC的代價(jià)是高昂的。如果大對(duì)象過(guò)多時(shí)诽凌,老年代很快就裝滿了烈和,這時(shí)就需要進(jìn)行Full GC,如果Full GC頻率過(guò)高皿淋,程序就會(huì)變得很卡招刹。
因此,對(duì)于大對(duì)象窝趣,有如下幾種處理方法:?
1. 在寫(xiě)程序的時(shí)候盡量避免大對(duì)象?
從源頭降低大對(duì)象的出現(xiàn)疯暑,盡量選擇空間利用率較高的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)。?
2. 盡量縮短大對(duì)象的有效時(shí)間?
對(duì)象用完后盡快讓它失效哑舒,好讓垃圾收集器盡快將他回收妇拯,避免因在新生代呆的時(shí)間過(guò)長(zhǎng)而進(jìn)入老年代。