1. 程式人生 > >JVM垃圾收集器(2)

JVM垃圾收集器(2)

此文已由作者趙計剛薪授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。


1、G1

20181206152157570c6197-bfe7-4375-98a8-492c8754bf2a.jpg


說明:

  • 從上圖來看,G1與CMS相比,僅在最後的"篩選回收"部分不同(CMS是併發清除),實際上G1回收器的整個堆記憶體的劃分都與其他收集器不同。

  • CMS需要配合ParNew,G1可單獨回收整個空間

原理:

  • G1收集器將整個堆劃分為多個大小相等的Region

  • G1跟蹤各個region裡面的垃圾堆積的價值(回收後所獲得的空間大小以及回收所需時間長短的經驗值),在後臺維護一張優先列表,每次根據允許的收集時間,優先回收價值最大的region,這種思路:在指定的時間內,掃描部分最有價值的region(而不是掃描整個堆記憶體),並回收,做到儘可能的在有限的時間內獲取儘可能高的收集效率。

運作流程:

  • 初始標記:標記出所有與根節點直接關聯引用物件。需要STW

  • 併發標記:遍歷之前標記到的關聯節點,繼續向下標記所有存活節點。

    • 在此期間所有變化引用關係的物件,都會被記錄在Remember Set Logs中

  • 最終標記:標記在併發標記期間,新產生的垃圾。需要STW

  • 篩選回收:根據使用者指定的期望回收時間回收價值較大的物件(看"原理"第二條)。需要STW

優點:

  • 停頓時間可以預測:我們指定時間,在指定時間內只回收部分價值最大的空間,而CMS需要掃描整個年老代,無法預測停頓時間

  • 無記憶體碎片:垃圾回收後會整合空間,CMS採用"標記-清理"演算法,存在記憶體碎片

  • 篩選回收階段:

    • 由於只回收部分region,所以STW時間我們可控,所以不需要與使用者執行緒併發爭搶CPU資源,而CMS併發清理需要佔據一部分的CPU,會降低吞吐量。

    • 由於STW,所以不會產生"浮動垃圾"(即CMS在併發清理階段產生的無法回收的垃圾)

適用範圍:

  • 追求STW短:若ParNew/CMS用的挺好,就用這個;若不符合,用G1

  • 追求吞吐量:用Parallel Scavenge/Parallel Old,而G1在吞吐量方面沒有優勢

 

2、幾點注意

問題1、G1以外的其他收集器在回收垃圾的時候:要不只是掃描年輕代,要不只是掃描年老代。在年輕代的回收過程中,如果舊生代中的物件引用了年輕代的物件,那麼我們只掃描年輕代就不行了,但是由於年老代一般而言是年輕代的3倍大小,如果年輕代、年老代一起去掃描的話,效率會急劇下降,這個問題怎麼處理?

:JVM採用remember set來做的這個事兒,當發現一個引用物件被賦值時,JVM發出一個write barrier指令來暫時中斷寫操作,檢查被賦值的引用物件是不是處於年老代,且其引用的物件是不是處於新生代(即是不是年老代物件引用了年輕代物件),如果是,將相關引用資訊記錄到remember set。之後的掃描,我們會從根集合+remember set向下進行掃描。(也就是說真正的根集合,是JVM定義的根集合+remember set)

 

問題2、G1收集器為了做到GC時間可預測,採用掃描部分價值最大的region來實現,那麼如果這部分region中的物件被其他region中的物件所引用,那麼僅掃描前者可能就不行了,但是如果掃描全部region的話,又無法做到GC時間可預測,效率會大大下降,怎麼辦?

:G1同理,為每一個region分配一個remember set,當發現一個引用物件被賦值時,JVM發出一個write barrier指令來暫時中斷寫操作,檢查被賦值的引用物件與其引用的物件是不是處於不同的region(eg.a=b;檢查a與b是不是在不同的region),如果是,將相關引用資訊記錄到當前region的remember set。之後的掃描,我們會從根集合+remember set向下進行掃描。

 

問題3、CMS與G1在併發標記的時候若發部分引用物件的引用關係發生了變化,怎麼處理才能讓重新標記的時候僅僅掃描出這些變化?

:在併發標記期間,物件的引用關係若發生了變化,這些相關的記錄就會記錄到remember set logs;在重新標記階段,將該logs的資訊加入到remember set中去,然後再從remember set去向下trace節點。


免費領取驗證碼、內容安全、簡訊傳送、直播點播體驗包及雲伺服器等套餐

更多網易技術、產品、運營經驗分享請點選


相關文章:
【推薦】 手把手帶你打造一個 Android 熱修復框架
【推薦】 客戶端SDK測試思路