垃圾回收(2)CMS

1、CMS介紹

全稱 Concurrent Mark Sweep,是一款併發的、使用標記-清除算法的垃圾回收器。算法

一、並行,STW時間短暫。
二、沒有壓縮和整理,產生內存碎片。數據結構

對象在標記過程當中,根據標記狀況,分紅三類:併發

一、白色對象,表示自身未被標記;
二、灰色對象,表示自身被標記,但內部引用未被處理;
三、黑色對象,表示自身被標記,內部引用都被處理;ide

垃圾回收(2)CMS

觸發時間:若是添加了一下參數oop

垃圾回收(2)CMS

當老年代的使用率達到80%時,就會觸發一次cms gc。線程

2、CMS的收集過程分爲6個步驟。

假設CMS GC以前的堆結構以下圖:
垃圾回收(2)CMS對象

一、初始標記(InitialMarking)

這是一個STW過程,主要分兩步
一、標記GC Roots可達的老年代對象;
二、遍歷GC Roots下的新生代對象可以可達的老年代對象;
三、此過程不對以上可達的老年代對象進行進一步的可達掃描。blog

結果:
垃圾回收(2)CMS遞歸

二、併發標記(Marking)

該階段GC線程和應用線程併發執行,遍歷InitialMarking階段標記出來的存活對象,而後繼續遞歸標記這些對象可達的對象。
這個過程應用線程在運行,可能Young GC也會發生,會發生如下的狀況:
一、新生代對象晉升到老年代
二、在老年代分配對象
三、新老年代對象的引用發生變化。內存

結果:

垃圾回收(2)CMS

2.一、那麼如何處理併發標記過程當中對象的變化呢?

CMS使用上一節講過的Card Table來解決這個問題
併發標記過程當中引用發生變化的對象所在的Card,在Card Table來記錄爲「髒卡」,這樣在後面從新標記的時候會把這些對象也當作GC Root來遍歷

可是Young GC若是發生,比方說:
一、併發標記還未掃描到髒卡1.
二、Young GC掃描完髒卡,並改變dirty到clean.
三、併發標記掃描,發現卡1已不是髒卡,則不會處理,這就形成了漏標。

2.二、若是解決以上的問題呢?

CMS中,有另外一種數據結構(Mod Union Table)
Mod Union Table是一個位向量,每一個單元的大小隻有1位,每一個單元對應一個Card(Card的大小是512字節,Card Table每個單元的大小是1個字節)
在新生代GC處理dirty card以前,先把該card在Mod Union Table裏面的對應項置位。
這樣,CMS在執行從新標記階段的時候,就會掃描Mod Union Table和card table裏面被標記的項。

三、預清理(Precleaning&AbortablePreclean)

3.1 Precleaning

經過參數CMSPrecleaningEnabled選擇關閉該階段,默認啓用,主要作兩件事情:

一、處理新生代已經發現的引用,好比在併發階段,在Eden區中分配了一個A對象,A對象引用了一個老年代對象B(這個B以前沒有被標記),在這個階段就會標記對象B爲活躍對象。
二、在併發標記階段,若是老年代中有對象內部引用發生變化,會把所在的Card標記爲Dirty(包括ModUnionTalble),經過掃描這些Table,從新標記那些在併發標記階段引用被更新的對象。

3.二、AbortablePreclean

該階段發生的前提是,新生代Eden區的內存使用量大於參數CMSScheduleRemarkEdenSizeThreshold 默認是2M,若是新生代的對象太少,就沒有必要執行該階段,直接執行從新標記階段。

爲何須要這個階段,存在的價值是什麼?

由於CMS GC的終極目標是下降垃圾回收時的暫停時間,因此在該階段要盡最大的努力去處理那些在併發階段被應用線程更新的老年代對象,這樣在暫停的從新標記階段就能夠少處理一些,暫停時間也會相應的下降。

在該階段,主要循環的作兩件事:

一、處理 From 和 To 區的對象,標記可達的老年代對象
二、和上一個階段同樣,掃描處理Dirty Card和ModUnionTalble中的對象。

固然了,這個邏輯不會一直循環下去,打斷這個循環的條件有三個:

一、能夠設置最多循環的次數 CMSMaxAbortablePrecleanLoops,默認是0,意思沒有循環次數的限制。
二、若是執行這個邏輯的時間達到了閾值CMSMaxAbortablePrecleanTime,默認是5s,會退出循環。
三、若是新生代Eden區的內存使用率達到了閾值CMSScheduleRemarkEdenPenetration,默認50%,會退出循環。

四、從新標記(STW的過程)

在以前的並行階段,可能產生新的引用關係以下:

一、老年代的新對象被GC Roots引用
二、老年代的未標記對象被新生代對象引用
三、老年代已標記的對象增長新引用指向老年代其它對象
四、新生代對象指向老年代引用被刪除

上述對象中可能有一些已經在Precleaning階段和AbortablePreclean階段被處理過,但總存在沒來得及處理的,因此須要作如下事情:

一、遍歷新生代對象,從新標記
二、根據GC Roots,從新標記
三、遍歷老年代的Dirty Card和Mod Union Table,從新標記

在第1步驟中,須要遍歷新生代的所有對象,若是新生代的使用率很高,須要遍歷處理的對象也不少,這對於這個階段的總耗時來講,是個災難(由於可能大量的對象是暫時存活的,並且這些對象也可能引用大量的老年代對象,形成不少應該回收的老年代對象而沒有被回收,遍歷遞歸的次數也增長很多),若是在這以前發生一次YGC,這樣就能夠避免掃描無效的對象。

CMS算法中提供了一個參數:CMSScavengeBeforeRemark,默認並無開啓,若是開啓該參數,在執行該階段以前,會強制觸發一次YGC,能夠減小新生代對象的遍歷時間,回收的也更完全一點。

五、併發清理

清理在標記階段收集標識爲不可達的對象

六、重置

清除數據結構,準備下一次併發收集。

相關文章
相關標籤/搜索