1. 程式人生 > >JVM必知必會(四)【收集器選擇,G1】

JVM必知必會(四)【收集器選擇,G1】

垃圾收集器選擇

JVM給了三種選擇:序列收集器、並行收集器、併發收集器,但是序列收集器只適用於小資料量的情況,所以這裡的選擇主要針對並行收集器和併發收集器。預設情況下,JDK5.0以前都是使用序列收集器,如果想使用其他收集器需要在啟動時加入相應引數。JDK5.0以後,JVM會根據當前系統配置進行判斷。

吞吐量優先的並行收集器

如上文所述,並行收集器主要以到達一定的吞吐量為目標,適用於科學技術和後臺處理等。

典型配置:

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelGC:

選擇垃圾收集器為並行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用併發收集,而年老代仍舊使用序列收集。
-XX:ParallelGCThreads=20:配置並行收集器的執行緒數,即:同時多少個執行緒一起進行垃圾回收。此值最好配置與處理器數目相等。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

-XX:+UseParallelOldGC:配置年老代垃圾收集方式為並行收集。JDK6.0支援對年老代並行收集。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-XX:+UseParallelGC -XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=100:設定每次年輕代垃圾回收的最長時間,如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

-XX:+UseAdaptiveSizePolicy:設定此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直開啟。

響應時間優先的併發收集器

如上文所述,併發收集器主要是保證系統的響應時間,減少垃圾收集時的停頓時間。適用於應用伺服器、電信領域等。

典型配置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC:設定年老代為併發收集。測試中配置這個以後,-XX:NewRatio=4的配置失效了,原因不明。所以,此時年輕代大小最好用-Xmn設定。

-XX:+UseParNewGC:設定年輕代為並行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設定,所以無需再設定此值。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction:由於併發收集器不對記憶體空間進行壓縮、整理,所以執行一段時間以後會產生“碎片”,使得執行效率降低。此值設定執行多少次GC以後對記憶體空間進行壓縮、整理。
-XX:+UseCMSCompactAtFullCollection:開啟對年老代的壓縮。可能會影響效能,但是可以消除碎片

Garbage First介紹

Garbage First簡稱G1,它的目標是要做到儘量減少GC所導致的應用暫停的時間,讓應用達到準實時的效果,同時保持JVM堆空間的利用率,其最大的特色在於允許指定在某個時間段內GC所導致的應用暫停的時間最大為多少,例如在100秒內最多允許GC導致的應用暫停時間為1秒,這個特性對於準實時響應的系統而言非常的吸引人,這樣就再也不用擔心繫統突然會暫停個兩三秒了。

目標

從設計目標看G1完全是為了大型應用而準備的。

支援很大的堆

高吞吐量
* 支援多CPU和垃圾回收執行緒
* 在主執行緒暫停的情況下,使用並行收集
* 在主執行緒執行的情況下,使用併發收集

實時目標:可配置在N毫秒內最多隻佔用M毫秒的時間進行垃圾回收
當然G1要達到實時性的要求,相對傳統的分代回收演算法,在效能上會有一些損失。

演算法詳解

這裡寫圖片描述
G1可謂博採眾家之長,力求到達一種完美。他吸取了增量收集優點,把整個堆劃分為一個一個等大小的區域(region)。記憶體的回收和劃分都以region為單位;同時,他也吸取了CMS的特點,把這個垃圾回收過程分為幾個階段,分散一個垃圾回收過程;而且,G1也認同分代垃圾回收的思想,認為不同物件的生命週期不同,可以採取不同收集方式,因此,它也支援分代的垃圾回收。為了達到對回收時間的可預計性,G1在掃描了region以後,對其中的活躍物件的大小進行排序,首先會收集那些活躍物件小的region,以便快速回收空間(要複製的活躍物件少了),因為活躍物件小,裡面可以認為多數都是垃圾,所以這種方式被稱為Garbage First(G1)的垃圾回收演算法,即:垃圾優先的回收。

回收步驟:

1. 初始標記(Initial Marking)

G1對於每個region都儲存了兩個標識用的bitmap,一個為previous marking bitmap,一個為next marking bitmap,bitmap中包含了一個bit的地址資訊來指向物件的起始點。

開始Initial Marking之前,首先併發的清空next marking bitmap,然後停止所有應用執行緒,並掃描標識出每個region中root可直接訪問到的物件,將region中top的值放入next top at mark start(TAMS)中,之後恢復所有應用執行緒。

觸發這個步驟執行的條件為:

G1定義了一個JVM Heap大小的百分比的閥值,稱為h,另外還有一個H,H的值為(1-h)*Heap Size,目前這個h的值是固定的,後續G1也許會將其改為動態的,根據jvm的執行情況來動態的調整,在分代方式下,G1還定義了一個u以及soft limit,soft limit的值為H-u*Heap Size,當Heap中使用的記憶體超過了soft limit值時,就會在一次clean up執行完畢後在應用允許的GC暫停時間範圍內儘快的執行此步驟;

在pure方式下,G1將marking與clean up組成一個環,以便clean up能充分的使用marking的資訊,當clean up開始回收時,首先回收能夠帶來最多記憶體空間的regions,當經過多次的clean up,回收到沒多少空間的regions時,G1重新初始化一個新的marking與clean up構成的環。

2.併發標記(Concurrent Marking)

按照之前Initial Marking掃描到的物件進行遍歷,以識別這些物件的下層物件的活躍狀態,對於在此期間應用執行緒併發修改的物件的以來關係則記錄到remembered set logs中,新建立的物件則放入比top值更高的地址區間中,這些新建立的物件預設狀態即為活躍的,同時修改top值。

3.最終標記暫停(Final Marking Pause)

當應用執行緒的remembered set logs未滿時,是不會放入filled RS buffers中的,在這樣的情況下,這些remebered set logs中記錄的card的修改就會被更新了,因此需要這一步,這一步要做的就是把應用執行緒中存在的remembered set logs的內容進行處理,並相應的修改remembered sets,這一步需要暫停應用,並行的執行。

4.存活物件計算及清除(Live Data Counting and Cleanup)

值得注意的是,在G1中,並不是說Final Marking Pause執行完了,就肯定執行Cleanup這步的,由於這步需要暫停應用,G1為了能夠達到準實時的要求,需要根據使用者指定的最大的GC造成的暫停時間來合理的規劃什麼時候執行Cleanup,另外還有幾種情況也是會觸發這個步驟的執行的:

G1採用的是複製方法來進行收集,必須保證每次的”to space”的空間都是夠的,因此G1採取的策略是當已經使用的記憶體空間達到了H時,就執行Cleanup這個步驟;

對於full-young和partially-young的分代模式的G1而言,則還有情況會觸發Cleanup的執行,full-young模式下,G1根據應用可接受的暫停時間、回收young regions需要消耗的時間來估算出一個yound regions的數量值,當JVM中分配物件的young regions的數量達到此值時,Cleanup就會執行;partially-young模式下,則會盡量頻繁的在應用可接受的暫停時間範圍內執行Cleanup,並最大限度的去執行non-young regions的Cleanup。