天天看點

[JVM] G1回收器(二)G1回收器(二)

G1回收器(二)

時間停頓模型:能夠在一個指定長度為M毫秒的時間段内,消耗在垃圾收集上的時間大機率不超過N毫秒

該模型就是G1回收器需要實作的目标

在G1之前,所有的垃圾回收器的回收目标都是一整個分代,如整個新生代,整個老年代,或者整個Java堆,G1則不同,它不再面向單個分代來進行垃圾回收,而是使用

回收集(Collection Set)

,回收集的組成可以是任意的堆記憶體,衡量回收集的标準是,哪塊記憶體存放垃圾最多,回收收益最大,這個就是G1獨有的

Mixed GC

模式

G1中同樣存在新生代、老年代的劃分,但是G1沒有使用之前的堆記憶體分布,而是将整個堆劃分為了多個大小相等的Region,每個Region都可以根據需要,被劃分給老年代或者新生代

G1中的大對象不再屬于老年代了,而是提供了

Humongous區域

專門存放大對象,對大對象的判斷也與之前不同了,之前是通過JVM參數

-XX:PretenureSizeThreshold

來控制,而G1中定義如果一個對象超過了單個Region的50%的記憶體,則這個對象就是大對象。每個Region的大小必須為2的N次幂,也可以通過參數

-XX:G1HeapRegionSize

,一個大對象可以橫跨多個Region

G1每次回收都是以Region為最小機關的,它會跟蹤每個Region的垃圾

回收的價值

大小,價值就是回收可以獲得的空間大小和回收所消耗的時間,然後G1會在背景維護一個優先級清單,每次根據使用者設定的允許停頓的最長時間(通過參數-XX:MaxGCPauseMillis指定,預設為200ms),優先回收那些價值最大的Region

G1如何解決跨Region引用的問題

同樣也是使用記憶集,卡表來避免全堆的GC Roots掃描,但是和之前在實作上不同

G1中的每個Region都會維護一個自己的記憶集,這些記憶集會記錄下其他Region指向自己的指針,并标記這些指針分别在哪些卡頁範圍内,其結構為一個哈希表,Key為其他Region的起始位址,Value為一個集合,集合中包含卡表的索引号,該結構存儲了誰指向我(Key)和我指向誰(Value)

每個Region都需要維護一個記憶集,是以G1比其他收集器所占用額外記憶體要多大約10%到20%

G1如何解決并發标記階段使用者線程與垃圾回收線程互相幹擾的情況

首先需要解決使用者線程在垃圾回收線程進行可達性分析時改變對象引用的問題。G1采用的是

原始快照(SATB)

的方式,當引用關系被改變,比如删除了,則記錄這次删除,在可達性分析完成後再以改變的節點為根節點重新進行掃描分析

其次要解決新對象配置設定記憶體的問題。G1為每個Region都添加了兩個指針TAMS(Top at Mark Start),把每個Region中的一部分空間劃分出來用于并發回收過程中的對象配置設定,并發期間所有的新配置設定的對象位址都要在這兩個指針的位置之上,G1預設在這個位址以上的對象是被

隐式标記

為存活的,不納入垃圾回收的範圍,與CMS中的

concurrent mode failure

相似,G1中也會出現并發時使用者線程建立對象因記憶體不足而觸發完全的基于serial old回收器的單線程Full GC,導緻長時間的STW

G1如何建立可靠的停頓預測模型

G1以

衰減均值

為理論基礎建立停頓預測模型,在垃圾回收過程中,G1會記錄每個Region的回收耗時,每個Region記憶集中的髒卡的數量等資料,通過分析析得出平均值、标準偏差、置信度等統計資訊,通過這些資料,才能夠決定哪些Region放入回收集才可以在不超過期望停頓時間的限制下獲得最高的效益

G1垃圾回收的步驟

總體分為四步

  • 初始标記:僅标記GC Roots能夠直接關聯到的對象,并且修改TAMS的值,讓下一節點并發時新對象能夠在可用的Region中配置設定,該步驟實際上是和Minor GC共同完成的,是以G1在這個階段幾乎沒有停頓
  • 并發标記:從GC Roots開始,掃描對象圖,進行可達性分析,找出需要回收的對象,與使用者線程并發,在掃描完成後需要處理SATB記錄下的在并發期間有改動引用的對象
  • 最終标記:需要短暫的停頓使用者線程,該階段用于處理并發期間留下的SATB記錄
  • 篩選回收:更新Region的統計資料,對Region的回收價值和成本進行排序,根據使用者指定的期望停頓時間指定回收計劃,選擇出多個Region來組成回收集,然後将決定回收的Region中的存活對象複制到空的Region中,再清理掉整個Region,該階段需要暫停使用者線程,并行執行

G1使用的回收算法

總體來看,G1是基于标記-整理算法來進行垃圾回收的,但是在局部(兩個Region之間)看來它是基于标記-複制算法來進行垃圾回收的,這兩種算法都不會産生記憶體碎片

繼續閱讀