JAVA之G1與CMS垃圾回收


G1 GC,全稱Garbage-FirstGarbage Collector,通過-XX:+UseG1GC參數來啟用,作為體驗版隨着JDK 6u14版本面世,在JDK 7u4版本發行時被正式推出,相信熟悉JVM的同學們都不會對它感到陌生。在JDK 9中,G1被提議設置為默認垃圾收集器(JEP 248)。那么與之前的CMS相比,G1有哪些改變,哪些優勢呢?

什么是CMS
CMS收集器是基於標記清除算法的一種並發的,低停頓的收集器,值得注意的一點是,CMS只是低停頓而不是沒有停頓

CMS分為以下四步:

l  初始標記

l  並發標記

l  重新標記

l  並發清除

初始標記和重新標記都是需要Stop the world的。

初始標記僅僅是記錄CG root關聯的對象,因此停頓時間比較短,並發標記是進行GC RootTracing,重新標記是修正並發標記期間標記的改動(時間比初始標記稍長一點),之后就是進行並發清除, 由於最耗時間的工作都是在並發操作中完成的,所以CMS的停頓會比較低

下面是CMS的過程圖 

 

 

 

CMS是一個優秀的垃圾回收器,但是他也有不少缺點:

l  他沒有辦法處理浮動垃圾(就是在初始標記之后產生的垃圾)

l  他會占用大量的CPU資源

l  他會產生碎片空間(當發生FullGC時會使用Serial Old回收器來處理碎片空間)

l  他在回收垃圾時,由於是並行的收集,所以需要的空間比較大

什么是G1
G1是一個並行回收器,它把堆內存分割為很多不相關的區間,每個區間可以屬於老年代或者年輕代,並且每個年齡代區間可以是物理上不連續的。老年代區間這個設計理念本身是為了服務於並行后台線程,這些線程的主要工作是尋找未被引用的對象,而這樣就會產生一種現象,即某些區間的垃圾(未被引用對象)多於其它的區間。垃圾回收時都是需要停下應用程序的,不然就沒辦法防止應用程序的干擾。G1 GC可以集中精力在垃圾最多的區間上,並且只費一點點時間就可以清空這些區間的垃圾,騰出完全空閑的區間。由於這種方式的側重點在於處理垃圾最多的區間,所以我們給G1一個名字:垃圾優先(Garbage First)。

G1內部有四個操作階段:

l  年輕代回收;(AYoung Collection)

l  運行在后台的並行循環;(ABackground,Concurrent Cycle)

l  混合回收;(A MixedCollection)

l  全量回收;(A FullGC)

 CMS與G1的分析比較
分代收集

這個現在是垃圾回收器的標配,G1和CMS也不例外。但是G1同時回收老年代和年輕代,而CMS只能回收老年代,需要配合一個年輕代收集器。另外G1的分代更多是邏輯上的概念,G1將內存分成多個等大小的region,Eden/ Survivor/Old分別是一部分region的邏輯集合,物理上內存地址並不連續。

CMS在old gc的時候會回收整個Old區,對G1來說沒有old gc的概念,而是區分Fullyyoung gc和Mixed gc,前者對應年輕代的垃圾回收,后者混合了年輕代和部分老年代的收集,因此每次收集肯定會回收年輕代,老年代根據內存情況可以不回收或者回收部分或者全部(這種情況應該是可能出現)。

如何處理跨代引用

在垃圾回收的時候都是從Root開始搜索,這會先經過年輕代再到老年代,對於年輕代引用老年代的這種跨代不需要單獨處理。但是老年代引用年輕代的會影響young gc,這種跨代需要處理。

為了避免在回收年輕代的時候掃描整個老年代,需要記錄老年代對年輕代的引用,young gc的時候只要掃描這個記錄。CMS和G1都用到了Card Table,但是用法不太一樣。JVM將內存分成一個個固定大小的card,然后有一個專門的數據結構(即這里的Card Table)維護每個Card的狀態,一個字節對應一個Card,有點像內存page的概念,只是page是硬件上的,Card Table是軟件上的。當一個Card上的對象的引用發生變化的時候,就將這個Card對應的Card Table上的狀態置為dirty,young gc的時候掃描狀態是dirty的Card即可。這是基本的用法,CMS基本上就是這么使用。

G1在Card Table的基礎上引入的rememberedset(下面簡稱RSet)。每個region都會維護一個RSet,記錄着引用到本region中的對象的其他region的Card。比如A對象在regionA,B對象在regionB,且B.f = A,則在regionA的RSet中需要記錄B所在的Card的地址。這樣的好處是可以對region進行單獨回收,這要求RSet不只是維護老年代到年輕代的引用,也要維護這老年代到老年代的引用,對於跨代引用的每次只要掃描這個region的RSet上的Card即可。

上面說過年輕代到老年代的引用不需要單獨處理,這帶來了很大的性能上的提升,因為年輕代的對象引用變化很大,如果都需要記錄下來成本會很高。同時也說明只需要在老年代維護Card Table。

如何處理並發過程的對象變化

CMS和G1都有並發處理過程,這個過程應用程序跟着gc線程一起運行,會產生新對象,也會有舊的對象死去,對象之間的引用關系也會發生變化。這部分數據可以暫時不處理,留到下一次再處理嗎?如果可以這樣的話問題就會變得很簡單,但是答案是不行。考慮下圖的場景(圖中每一行表示一個內存狀態,每一列表示一個Card,這里有4個):第一步a是並發標記中途的一個狀態,標記了a b c e四個對象,0 1兩個Card已經標記好;第二步b並發標記的同時引用發生變化,g不再指向d,而b不再指向c,變成指向d,這個時候處理Card 2,會標記到g,然后就標記結束了,導致d對象丟失。

CMS初始標記的時候會標記所有從root直接可達的對象,並發標記的時候再從這些對象進一步搜索其他可達對象,最終構成一個存活的對象圖。並發標記過程中引用發生變化的也是通過Card Table來記錄。但是young gc的時候如果一個dirty card沒有包含到年輕代的引用,這個card會重新標記為clean,這有可能將並發標記過程產生的dirty card錯誤清除,因此CMS引入了另一個數據結構mod union table,這里一個bit對應一個Card,young gc在將Card Table設置為clean的時候會將對應的mod union table置為dirty。最終標記的時候會將Card Table或者mod union table是dirty的Card也作為root去掃描,從而解決並發標記過程產生的引用變化。CMS還需要處理並發過程從年輕代晉升到老年代的對象,處理方式是將這部分對象也作為root去掃描。

G1使用一個稱為snapshot at thebeginning(下面簡稱SATB)的算法,在初始標記的時候得到一個從root直接可達的snapshot,之后從這個snapshot不可達的對象都是可以回收的垃圾,並發過程產生的對象都默認是活的對象,留到下一次再處理。對於引用關系發生變化的,將這個對象對應的Card放到一個SATB隊列里,在最終標記的時候進行處理(如果超過一定的閾值並發標記的時候也會處理一部分),處理的過程就是以隊列中的Card作為root進行掃描。

Write Barrier

Write Barrier可以理解為在寫的時候插入一條特定的操作。

在CMS中老年代引用年輕代的時候就是通過觸發一個Write Barrier來更新Card Table的標志位。這是一個同步操作,在更新引用的時候順帶執行,只需要兩個指令,引入的消耗不大。

G1比較復雜,在兩個地方用到了WriteBarrier,分別是更新RSet的rememberd set Write Barrier和記錄引用變化的ConcurrentMarking Write Barrier,前者發生在引用更新之后,稱為Post Write Barrier,后者發生在引用變化之前,稱為Pre Write Barrier。G1為了提高性能,這兩個Write Barrier都是先放到隊列中,再異步進行處理。

Full GC

導致CMS Full GC的可能原因主要有兩個:Promotion Failure和Concurrent Mode Failure,前者是在年輕代晉升的時候老年代沒有足夠的連續空間容納,很有可能是內存碎片導致的;后者是在並發過程中jvm覺得在並發過程結束前堆就會滿了,需要提前觸發Full GC。CMS的Full GC是一個多線程STW的Mark-Compact過程,,需要盡量避免或者降低頻率。

G1的初衷就是要避免Full GC的出現,Full GC會會對所有region做Evacuation-Compact,而且是單線程的STW,非常耗時間。導致G1Full GC的原因可能有兩個:1. Evacuation的時候沒有足夠的to-space來存放晉升的對象;2. 並發處理過程完成之前空間耗盡。這兩個原因跟CMS類似。
————————————————
版權聲明:本文為CSDN博主「HelloWorld搬運工」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wufaliang003/article/details/80684379


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM