1.什么是CMS
CMS主要設計目標:低停頓時間
CMS全稱ConcurrentMarkSweep,作用於老年代,並發標記清除,並發是指垃圾回收和應用程序同時運行,這樣的目的是為了降低STW的時間(200ms)
CMS垃圾回收器基於標記-清除算法實現,那么使用該算法的最大缺點也顯而易見——大量的內存碎片。內存碎片過多時會給大對象分配帶來麻煩,即會存在空間足夠,但是連續的空間太小,這樣的話就會觸發Full GC,CMS發送FGC就會使用SerialOld進行老年代回收,這個過程如果內存比較大的話,就會產生很長的STW(幾個小時-幾天的STW)
CMS解決內存碎片的辦法:使用 -XX:CMSFullGCsBefore-Compaction(JDK9之后廢棄),即CMS在並發執行若干此Full GC之后,下一次Full GC會先進行碎片整理。(默認為0,即每次都整理)
2.CardTable
由於做YGC時,需要掃描整個OLD區,效率非常低,所以JVM設計了CardTable, 如果一個OLD區CardTable中有對象指向Y區,就將它設為Dirty,下次掃描時,只需要掃描Dirty Card所指向的內存區域即可。在結構上,Card Table用BitMap來實現
參考鏈接:
3.CMS的7個階段
CMS收集器的GC周期主要由7個階段組成,其中有兩個階段會發生stop-the-world,其他階段都是並發執行的。(亦有4個階段、6個階段等說法)
Phase 1: Initial Mark(初始化標記):
初始化標記階段,是CMS GC的第一個階段,也是標記階段的開始。主要工作是標記可直達的存活對象。
主要標記過程:
1.標記GCRoots能直接關聯到的對象,由於這種對象比較少,所以標記速度快
2.遍歷被新生代存活對象所引用的老年代對象
程序執行情況:
1.支持單線程或並行標記
2.發生stop-the-world,暫停所有應用線程
(Marked obj:老年代綠色圓點表示被初始化標記的對象。)
Phase 2: Concurrent Mark(並發標記):
並發標記階段,是CMS GC的第二個階段。在該階段,GC線程和應用線程將並發執行。也就是說,在第一個階段(Initial Mark)被暫停的應用線程將恢復運行。
並發標記階段的主要工作是,通過遍歷第一個階段(Initial Mark)標記出來的存活對象,繼續遞歸遍歷老年代,並標記可直接或間接到達的所有老年代存活對象。
(Current obj:該對象的引用關系發生變化,對下一個對象的引用被刪除。)
由於在並發標記階段,應用線程和GC線程是並發執行的,因此可能產生新的對象或對象關系發生變化,例如:
- 新生代的對象晉升到老年代;
- 直接在老年代分配對象;
- 老年代對象的引用關系發生變更等等
對於這些對象,需要重新標記以防止被遺漏。為了提高重新標記的效率,本階段會把這些發生變化的對象所在的Card標識為Dirty,這樣后續就只需要掃描這些Dirty Card的對象,從而避免掃描整個老年代。
Phase 3: Concurrent Preclean(並發預清理):
在並發預清洗階段,將會重新掃描前一個階段標記的Dirty對象,並標記被Dirty對象直接或間接引用的對象,然后清除Card標識。
標記被Dirty對象直接或間接引用的對象:
清除Dirty對象的Card標識:
Phase 4: Concurrent Abortable Preclean(可中止的並發預清理):
本階段盡可能承擔更多的並發預處理工作,從而減輕在Final Remark階段的stop-the-world。
在該階段,主要循環的做兩件事:
1.處理 From 和 To 區的對象,標記可達的老年代對象;
2.和上一個階段一樣,掃描處理Dirty Card中的對象。
具體執行多久,取決於許多因素,滿足其中一個條件將會中止運行:
1.執行循環次數達到了閾值;
2.執行時間達到了閾值;
3.新生代Eden區的內存使用率達到了閾值。
Phase 5: Final Remark(重新標記):
預清理階段也是並發執行的,並不一定是所有存活對象都會被標記,因為在並發標記的過程中對象及其引用關系還在不斷變化中。
因此,需要有一個stop-the-world的階段來完成最后的標記工作,這就是重新標記階段(CMS標記階段的最后一個階段)。主要目的是重新掃描之前並發處理階段的所有殘留更新對象。
主要工作:
- 遍歷新生代對象,重新標記;(新生代會被分塊,多線程掃描)
- 根據GC Roots,重新標記;
- 遍歷老年代的Dirty Card,重新標記。這里的Dirty Card,大部分已經在Preclean階段被處理過了。
Phase 6: Concurrent Sweep(並發清理):
並發清理階段,主要工作是清理所有未被標記的死亡對象,回收被占用的空間。
Phase 7: Concurrent Reset(並發重置):
並發重置階段,將清理並恢復在CMS GC過程中的各種狀態,重新初始化CMS相關數據結構,為下一個垃圾收集周期做好准備。
4.CMS采用的算法
采用三色標記 + Incremental Update
5.CMS的優缺點
-
優點
低停頓時間
並發收集
-
缺點
1.內存碎片(Memory Fragmentation):內存碎片過多時會給大對象分配帶來麻煩,即會存在空間足夠,但是連續的空間太小,這樣的話就會提前觸發Full GC。CMS解決內存碎片的辦法使用:-XX:CMSFullGCsBeforeCompaction參數(默認為0 指的是經過多少次FGC才進行壓縮)
2.浮動垃圾(Floating Garbage):可能出現“Concurrent Mode Failure”導致另一次Full GC的產生。在並發標記階段由於程序的工作線程和垃圾收集線程是同時運行或交叉運行,那么在並發標記階段如何產生新的垃圾對象,CMS將無法對這些垃圾對象進行標記,最終會導致這些新產生的垃圾對象沒有被及時回收,從而只能在下一次執行GC的時釋放這些之前未被回收的內存空間(浮動垃圾過多會導致分配對象時內存不足從而觸發Full GC)。解決方法:降低觸發CMS的閾值,–XX:CMSInitiatingOccupancyFraction 92% 可以降低這個值,讓CMS保持老年代足夠的空間
3.對CPU資源敏感:在並發階段它雖然不會導致用戶停頓,但是會因為占用了一部分線程而導致應用程序變慢,總吞吐量會降低
CMS只要觸發FGC,就會讓SerialOld(單線程)進行老年代回收,這個過程如果內存比較大的話,就會產生很長的STW(幾個小時-幾天的STW)