天天看點

[JVM] G1回收器介紹G1回收器

G1回收器

在使用ParNew + CMS進行垃圾回收時,會導緻長時間的

STW

,而STW的本質就是停止系統的工作線程,讓系統無法對外服務,同時讓GC線程進行垃圾回收的。如果回收時間較長,那麼會給系統性能帶來較大的影響

是以G1回收器出現了,它的特點有

  • 将Java堆分為多個大小相等的

    Region

  • 每個

    Region

    都有可能屬于新生代或老年代
  • 可以設定

    垃圾回收的預期停頓時間

    ,比如在一小時内最多停頓一分鐘

G1如何做到設定

垃圾回收的預停頓時間

G1會追蹤每個

Region

中的

回收價值(目前Region中有多少垃圾對象,需要耗費多長時間)

,通過追蹤,G1可以比較出本次回收哪些

Region

的成本效益最高(時間最短,回收記憶體最多),在有限的時間内盡可能回收掉更多的垃圾對象

如何啟用G1回收器

可以通過JVM參數

-XX:UseG1GC

來指定垃圾回收器為G1

如何設定G1的記憶體大小

在指定了G1回收器後,它會根據

-Xmx/2048

來計算每個Region的記憶體大小,因為JVM中最多支援2048個Region,并且每個Region的記憶體必須是2的倍數

比如你設定的

-Xmx4096M

,那麼按照G1的規則來,4096 / 2048 = 2MB,那麼每個Region的記憶體大小就為2MB

當然,你也可以手動通過JVM參數

-XX:G1HeapRegionSize

來指定每個Region的大小

系統初始化狀态,新生代的記憶體占比為5%,大概為100個Region,可以通過JVM參數

-XX:G1NewSizePercent

來設定這個比例,但是一般都推薦保持這個設定,因為G1會動态的調整新生代、老年代的記憶體占比,但是預設情況下新生代的最大占比不能超過

60%

,可以通過JVM參數

-XX:G1MaxNewSizePercent

來設定這個比例

G1中新生代還保留着Eden、Survivor的劃分

雖然G1回收器将記憶體分為了多個Region,但是新生代依舊保留着原本的區域劃分,即Eden、Survivor1、Survivor2,并且預設情況下它們的比例為

8:1:1

,可以通過JVM參數

-XX:SurvivorRatio

來設定比例,比如系統初始化時,新生代大約有100個Region,那麼其中大約有80個Region是Eden區的,10個Region是S1區的,10個Region是S2區的

G1中新生代的垃圾回收算法

G1中新生代在Eden區滿時會觸發GC,垃圾回收算法還是使用

複制算法

,同時進入

STW

,但是因為G1設定了預期停頓時間,可以讓系統在GC時指定最多停頓的時間,而不像ParNew回收器那樣,極端情況下能夠一直停頓,而對系統性能造成嚴重影響

可以通過JVM參數

-XX:MaxGCPauseMills

來設定這個時間

G1中對象什麼時候進入老年代

和原來一樣,進入老年代的條件有,

  1. 超過指定年齡的對象

    ,預設為15年,通過JVM參數

    -XX:MaxTenuringThreshold

    指定這個門檻值
  2. 動态年齡判斷

    ,當某一年齡

    以下(包括)

    的對象占有了新生代記憶體的

    50%

    以上,那麼所有超過該年齡的對象都會進入老年代
  3. 新生代GC後,存活對象記憶體大小超過Survivor區記憶體大小,這些對象會進入老年代

G1中如何處理大對象

在G1中,大對象不會進入老年代,G1會提供專門的Region來存放這些大對象

G1中大對象的判定規則為:該對象超過了一個Region大小的50%,那麼這個對象就是大對象

大對象可能會橫跨多個Region

G1中大對象的回收

會在新生代、老年代發生GC時順帶回收掉

什麼時候觸發新生代、老年代混合垃圾回收

JVM參數

-XX:InitiatingHeapOccupancyPercent

,預設為45,即當老年代占堆記憶體比例達到45%時,即Region數量大約為1000個時,會嘗試觸發一個混合回收

Mixed GC

G1回收器的回收過程

  • 初始标記
  • 并發标記
  • 最終标記
  • 混合回收

初始标記

和CMS一樣,這個步驟會觸發

STW

,此時會标記那些被

GC Roots

直接引用的對象,比如靜态變量,局部變量等

并發标記

這個步驟,工作線程和GC線程會同時工作,此時會追蹤所有存活的對象,同時JVM會對這個步驟中對象的一些修改記錄起來,比如哪個對象被建立了,哪個對象失去了引用等

最終标記

這個步驟會觸發

STW

,對并發标記階段記錄的被修改的對象進行最終标記,找出存活對象和垃圾對象

混合回收

這個步驟會計算老年代中每個Region中存活的對象數量,存活對象的占比和執行垃圾回收的預期性能和效率,然後停止工作線程,對部分Region進行回收,因為G1需要控制垃圾回收時的停頓時間(使用者指定)

混合回收的區域包括新生代、老生代和大對象Region

這個階段會執行多次,JVM參數

-XX:G1MixedGCCountTarget

指定了混合回收執行的次數,預設為8次,每執行完一次會啟動工作線程對外提供服務,一段時間後繼續進行

STW

,執行混合回收,這樣可以盡可能的讓系統停頓的時間變短,盡量減少對象系統性能的影響

JVM參數

-XX:G1HeapWastePercent

,預設為5,當G1中Region空閑比例達到5%時,混合回收就結束了

在混合回收時,對Region的回收都是基于

複制算法

的,即把Region中的存活對象複制到其他Region中,然後清理掉目前Region中的垃圾對象,那麼G1回收器進行垃圾回收時,是不會産生記憶體碎片的

JVM參數

-XX:G1MixedGCLiveThresholdPercent

,預設為85,G1回收器在确定回收Region時,必須要保證該Region中的存活對象在85%以下,否則使用複制算法的回收效率是非常低的

當G1回收失敗時,如在回收時拷貝對象的時候,回收器發現沒有空閑的Region可以放入存活對象了,那麼就會觸發一次回收失敗,失敗之後,JVM就會切換到單線程回收器,進行标記-清理,壓縮和整理,空閑出一批Region,這個過程是非常慢的,又因為它是需要

STW

的,是以對系統的性能影響非常大

繼續閱讀