天天看點

Java的經典垃圾收集器G1,如何設定參數,什麼場景下适用?

作者:貴哥說Java創業

G1 (Garbage-First)是一款面向伺服器的垃圾收集器,主要針對配備多顆處理器及大容量記憶體的機器. 以極高機率滿足GC停頓時間要求的同時,還具備高吞吐量性能特征。

Java的經典垃圾收集器G1,如何設定參數,什麼場景下适用?

G1将Java堆劃分為多個大小相等的獨立區域(Region),JVM最多可以有2048個Region。一般Region大小等于堆大小除以2048,比如堆大小為4096M,則Region大小為2M,當然也可以用參數"-XX:G1HeapRegionSize"手動指定Region大小,但是推薦預設的計算方式。

G1保留了年輕代和老年代的概念,但不再是實體隔閡了,它們都是(可以不連續)Region的集合。預設年輕代對堆記憶體的占比是5%,如果堆大小為4096M,那麼年輕代占據200MB左右的記憶體,對應大概是100個Region,可以通過“-XX:G1NewSizePercent”設定新生代初始占比。

在系統運作中,JVM會不停的給年輕代增加更多的Region,但是最多新生代的占比不會超過60%,可以通過“-XX:G1MaxNewSizePercent”調整。年輕代中的Eden和Survivor對應的region也跟之前一樣,預設8:1:1,假設年輕代現在有1000個region,eden區對應800個,s0對應100個,s1對應100個。

一個Region可能之前是年輕代,如果Region進行了垃圾回收,之後可能又會變成老年代,也就是說Region的區域功能可能會動态變化。

G1垃圾收集器對于對象什麼時候會轉移到老年代跟之前講過的原則一樣,唯一不同的是對大對象的處理。

G1有專門配置設定大對象的Region叫Humongous區,而不是讓大對象直接進入老年代的Region中。在G1中,大對象的判定規則就是一個大對象超過了一個Region大小的50%,比如按照上面算的,每個Region是2M,隻要一個大對象超過了1M,就會被放入Humongous中,而且一個大對象如果太大,可能會橫跨多個Region來存放。

Humongous區專門存放短期巨型對象,不用直接進老年代,可以節約老年代的空間,避免因為老年代空間不夠的GC開銷。

Full GC的時候除了收集年輕代和老年代之外,也會将Humongous區一并回收。

G1收集器一次GC的運作過程大緻分為以下幾個步驟:

初始标記(initial mark,STW):暫停所有的其他線程,并記錄下gc roots直接能引用的對象,速度很快 ;

并發标記(Concurrent Marking):同CMS的并發标記。

最終标記(Remark,STW):同CMS的重新标記。

篩選回收(Cleanup,STW):篩選回收階段,首先對各個Region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間(可以用JVM參數 -XX:MaxGCPauseMillis指定)來制定回收計劃。

比如說老年代此時有1000個Region都滿了,但是因為根據預期停頓時間,本次垃圾回收可能隻能停頓200毫秒,那麼通過之前回收成本計算得知,可能回收其中800個Region剛好需要200ms,那麼就隻會回收800個Region(Collection Set,要回收的集合),盡量把GC導緻的停頓時間控制在我們指定的範圍内。

這個階段其實也可以做到與使用者程式一起并發執行,但是因為隻回收一部分Region,時間是使用者可控制的,而且停頓使用者線程将大幅提高收集效率。不管是年輕代或是老年代,回收算法主要用的是複制算法,将一個region中的存活對象複制到另一個region中,這種不會像CMS那樣回收完因為有很多記憶體碎片還需要整理一次,G1采用複制算法回收幾乎不會有太多記憶體碎片。(注意:CMS回收階段是跟使用者線程一起并發執行的,G1因為内部實作太複雜暫時沒實作并發回收,不過到了Shenandoah就實作了并發收集,Shenandoah可以看成是G1的更新版本)

Java的經典垃圾收集器G1,如何設定參數,什麼場景下适用?

G1收集器在背景維護了一個優先清單,每次根據允許的收集時間,優先選擇回收價值最大的Region(這也就是它的名字Garbage-First的由來),比如一個Region花200ms能回收10M垃圾,另外一個Region花50ms能回收20M垃圾,在回收時間有限情況下,G1當然會優先選擇後面這個Region回收。這種使用Region劃分記憶體空間以及有優先級的區域回收方式,保證了G1收集器在有限時間内可以盡可能高的收集效率。

G1被視為JDK1.7以上版本Java虛拟機的一個重要進化特征。它具備以下特點:

(1)并行與并發:G1能充分利用CPU、多核環境下的硬體優勢,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程來執行GC動作,G1收集器仍然可以通過并發的方式讓java程式繼續執行。

(2)分代收集:雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但是還是保留了分代的概念。

空間整合:與CMS的“标記--清理”算法不同,G1從整體來看是基于“标記整理”算法實作的收集器;從局部上來看是基于“複制”算法實作的。

(3)可預測的停頓:這是G1相對于CMS的另一個大優勢,降低停頓時間是G1 和 CMS 共同的關注點,但G1 除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明确指定在一個長度為M毫秒的時間片段(通過參數"-XX:MaxGCPauseMillis"指定)内完成垃圾收集。

毫無疑問, 可以由使用者指定期望的停頓時間是G1收集器很強大的一個功能, 設定不同的期望停頓時間, 可使得G1在不同應用場景中取得關注吞吐量和關注延遲之間的最佳平衡。 不過, 這裡設定的“期望值”必須是符合實際的, 不能異想天開, 畢竟G1是要當機使用者線程來複制對象的, 這個停頓時間再怎麼低也得有個限度。

它預設的停頓目标為兩百毫秒, 一般來說, 回收階段占到幾十到一百甚至接近兩百毫秒都很正常, 但如果我們把停頓時間調得非常低, 譬如設定為二十毫秒, 很可能出現的結果就是由于停頓目标時間太短, 導緻每次選出來的回收集隻占堆記憶體很小的一部分, 收集器收集的速度逐漸跟不上配置設定器配置設定的速度, 導緻垃圾慢慢堆

積。 很可能一開始收集器還能從空閑的堆記憶體中獲得一些喘息的時間, 但應用運作時間一長就不行了, 最終占滿堆引發Full GC反而降低性能, 是以通常把期望停頓時間設定為一兩百毫秒或者兩三百毫秒會是比較合理的。

G1收集器參數設定

-XX:+UseG1GC:使用G1收集器

-XX:ParallelGCThreads:指定GC工作的線程數量

-XX:G1HeapRegionSize:指定分區大小(1MB~32MB,且必須是2的N次幂),預設将整堆劃分為2048個分區

-XX:MaxGCPauseMillis:目标暫停時間(預設200ms)

-XX:G1NewSizePercent:新生代記憶體初始空間(預設整堆5%)

-XX:G1MaxNewSizePercent:新生代記憶體最大空間

-XX:TargetSurvivorRatio:Survivor區的填充容量(預設50%),Survivor區域裡的一批對象(年齡1+年齡2+年齡n的多個年齡對象)總和超過了Survivor區域的50%,此時就會把年齡n(含)以上的對象都放入老年代

-XX:MaxTenuringThreshold:最大年齡門檻值(預設15)

-XX:InitiatingHeapOccupancyPercent:老年代占用空間達到整堆記憶體門檻值(預設45%),則執行新生代和老年代的混合收集(MixedGC),比如我們之前說的堆預設有2048個region,如果有接近1000個region都是老年代的region,則可能就要觸發MixedGC了

-XX:G1MixedGCLiveThresholdPercent(預設85%) region中的存活對象低于這個值時才會回收該region,如果超過這個值,存活對象過多,回收的的意義不大。

-XX:G1MixedGCCountTarget:在一次回收過程中指定做幾次篩選回收(預設8次),在最後一個篩選回收階段可以回收一會,然後暫停回收,恢複系統運作,一會再開始回收,這樣可以讓系統不至于單次停頓時間過長。

-XX:G1HeapWastePercent(預設5%): gc過程中空出來的region是否充足門檻值,在混合回收的時候,對Region回收都是基于複制算法進行的,都是把要回收的Region裡的存活對象放入其他Region,然後這個Region中的垃圾對象全部清理掉,這樣的話在回收過程就會不斷空出來新的Region,一旦空閑出來的Region數量達到了堆記憶體的5%,此時就會立即停止混合回收,意味着本次混合回收就結束了。

G1垃圾收集器優化建議

假設參數 -XX:MaxGCPauseMills 設定的值很大,導緻系統運作很久,年輕代可能都占用了堆記憶體的60%了,此時才觸發年輕代gc。

那麼存活下來的對象可能就會很多,此時就會導緻Survivor區域放不下那麼多的對象,就會進入老年代中。

或者是你年輕代gc過後,存活下來的對象過多,導緻進入Survivor區域後觸發了動态年齡判定規則,達到了Survivor區域的50%,也會快速導緻一些對象進入老年代中。

是以這裡核心還是在于調節 -XX:MaxGCPauseMills 這個參數的值,在保證他的年輕代gc别太頻繁的同時,還得考慮每次gc過後的存活對象有多少,避免存活對象太多快速進入老年代,頻繁觸發mixed gc.

什麼場景适合使用G1

1. 50%以上的堆被存活對象占用。

2. 對象配置設定和晉升的速度變化非常大。

3. 垃圾回收時間特别長,超過1秒。

4. 8GB以上的堆記憶體(建議值)。

5. 停頓時間是500ms以内。

繼續閱讀