JVM調優(二)內存收集器原理和參數調整

調優

根據上節的原理分析,進行調優可以從硬件、操作系統、JVM以及程序四個方面入手。

用更好的硬件如更大的內存、更快的CPU等,操作系統的話,需要自己去摸索,window不行就換linux,linux不行就換unix,甚至其它商業機。對於這兩點,如果程序確實解決不了,可以把問題拋給運維。但作爲一名開發者,我們現在可以從JVM和程序兩方面嘗試優化。

JVM方面,從內存分配管理、GC策略入手。

先搞清楚垃圾收集的幾個重要概念:

並行(Parallel):指多條垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態。

併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不一定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行於另一個CPU上。

新生代GC(Minor GC):指發生在新生代的垃圾收集動作,因爲Java對象大多都具備朝生夕滅的特性,所以Minor GC非常頻繁,一般回收速度也比較快。

老年代GC(Major GC / Full GC):指發生在老年代的GC,出現了Major GC,經常會伴隨至少一次的Minor GC。Major GC的速度一般會比Minor GC慢10倍以上。

吞吐量:就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 /(運行用戶代碼時間 + 垃圾收集時間)。虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。

簡單分析各GC收集器:

Serial收集器是最基本的收集器。簡單高效。單線程跑,需要stop the world。默認用於JVM client模式的新生代。使用標記-整理算法。

ParNew收集器其實就是Serial收集器的多線程版本,也需要stop the world。是JVM server模式的新生代的優先選擇。(很重要的原因:除了Serial收集器外,目前只有它能與CMS收集器配合工作)。使用標記-整理算法。

CMS收集器收集器是HotSpot虛擬機中第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集線程與用戶線程同時工作。但CMS作爲老年代的收集器,無法與新生代收集器Parallel Scavenge配合工作。基於“標記—清除”算法。需要經歷:初始標記(CMS initial mark,非常快,stop the world)、併發標記(CMS concurrent mark,慢)、重新標記(CMS remark,快,stop the world)、併發清除(CMS concurrent sweep,慢),四個階段。CMS收集器對CPU資源非常敏感,產生內存碎片,無法清除浮動垃圾。

Parallel Scavenge收集器是一個新生代收集器,它也是使用複製算法的收集器,又是並行的多線程收集器,高吞吐量。“標記-整理”算法。

Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用標記-整理算法。

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法。

G1是面向服務端的,將來替換CMS。基於並行和併發、低延遲以及暫停時間更加可控的區域化分代式垃圾收集器。 G1 重塑了整個Java堆區,不再區分新生代和老年代,而是將Java堆區分成約2048 個大小相同的獨立Region 塊,每個Region 塊可能連續,也可能不連續,大小被控制在1MB~32MB之間。這是因爲G1收集器在執行內存回收時,能夠優先釋放掉整個Java堆區中一些佔用內存較大的Region塊,而無需像其他收集器一樣直接掃描整個java堆區,因此能更好地提升GC的回收效率和縮短”Stop-the-World” 機制的暫停時間以換取更大程序吞吐量

G1回收步步驟:1、所有Eden區清空,Survival和Older區增大。2、併發標記,標出垃圾最多的一些O區,過程:掃描root,併發標記,二次標記階段和清理階段。3、混合GC,就是清理階段。可以查看G1收集器原理,說的非常詳細。

收集算法:

標記-複製(標記複製清理)、標記-清理(標記清理)、標記-整理(標記清理移動)。

介紹完這些內容,下面開始講優化方案。

代大小調優

關注參數:-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold

-Xms(最小堆內存)和-Xmx(最大堆內存)設置相同值,避免堆內存不斷申請擴大。

-Xmn(新生代內存):應與堆內存裏的老年代內存比率爲1:2。而新生代裏又有Eden和Survivor(S0,S1,比率1:1)。Eden與S0(或S1)的比例關係用-XX:SurvivorRation來控制。

-XX:MaxTenuringThreshold:控制新生代裏對象經歷多少次MinorGC後才轉入舊生代(不是轉入Survivor區,每次MinorGC後存活對象的生存點是Survivor區)。

避免將新生代內存設置太小,否則:1、容易頻繁引起MinorGC,2、更多的新生代對象直接進入老年代,更易引起FullGC。

調大JVM Heap內存,並增大新生代內存。儘量讓對象在MinorGC階段被回收。但Jvm Heap內存增大後,單次GC的時間就會增加。新生代相對調大後(MinorGC時間變長),舊生代變小,易引起FullGC。所以在調優時可以多試幾次以取得更好效果。

新生代的Survivor區域要合適。太大造成Eden空間小,容易MinorGC;太小MinorGC後對象直接進入老年代易引起FullGC。

內存比較合理的情況下,可以試着調大新生代的存活週期。

GC策略調優

串行GC性能較差,實際環境以並行和併發GC爲主。

算法上“以時間換空間,以空間換時間”,這句話在這裏其實也是適用。

對於延遲比較嚴格的操作,可以採用CMS收集器(標記清除,快),能夠減少GC動作對應用的暫停時間。

優化時,本着減少FullGC爲首要原則。然後,MinorGC頻繁執行會造成cpu的us佔用過高。

總得來說,JVM的調優是一個經驗問題,要反覆去調節,才能達有滿意的效果。當然除此之外,還受限於公司成本、公司制度等原因。_

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章