一、簡介
Concurrent Mark Sweep,是一種以獲取最短回收停頓時間為目標的收集器,尤其重視服務的響應速度。
CMS是老年代垃圾回收器,基於標記-清除算法實現。新生代默認使用ParNew收集器,基於復制算法
二、垃圾回收過程
分為四個步驟進行垃圾回收:初始標記,並發標記,重新標記,並發清除。只有初始標記和重新標記需要停頓。
- 初始標記。只是標記一下GC Roots能直接關聯到的老年代對象,速度很快。這一階段會STW
- 並發標記。就是進行GC Roots的Tracing,處理器可以與用戶線程一起工作。
- 重新標記。為了修正並發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段也會觸發STW,停頓時間會比初始標記階段稍長,遠比並發時間短。
- 並發清除,處理器可以與用戶線程一起工作。
可達性分析方法
- 通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為“引用鏈”,當一個對象到GC Roots沒有任何引用鏈相連時,說明這個對象是可回收的。
- Java語言中,可作為GC Roots的對象包括以下幾種:虛擬機棧中引用的對象,方法區中類靜態屬性引用的對象,方法區中常量引用的對象,本地方法棧中引用的對象。
三、垃圾回收觸發時機
新生代垃圾回收觸發時機:當 eden 區內存無法為一個新對象分配內存時,就會觸發 Minor GC
老年代垃圾回收觸發時機:
2、老年代使用率達到閾值
CMSInitiatingOccupancyFraction,默認92%。
3、永久代的使用率達到閾值
CMSInitiatingPermOccupancyFraction,默認92%,前提是開啟
CMSClassUnloadingEnabled。
4、新生代的晉升擔保失敗。老年代沒有足夠的空間來容納全部的新生代對象或歷史平均晉升到老年代的對象,如果不夠的話,就提早進行一次老年代的回收,防止下次進行YGC的時候發生晉升失敗。
四、垃圾回收如何做到STW
JVM有個叫做“安全點”和“安全區域”的東西,在發生GC時,所有的線程都會執行到“安全點”停下來。
在需要GC的時候,JVM會設置一個標志,當線程執行到安全點的時候會輪詢檢測這個標志,如果發現需要GC,則線程會自己掛起,直到GC結束才恢復運行。
但是對於一些沒有獲得或無法獲得CPU時間的線程,就沒辦法等到它執行到安全點了,所以這個時候只要這個線程是在安全區域的,也可以進行GC,安全區域是一段代碼段,在這段代碼段中對象的引用關系不會發生變化,所以這個時候進行GC也是安全的。
五、關於FullGC
- Full GC == Major GC指的是對老年代/永久代的stop the world的GC
- Full GC的次數 = 老年代GC時 stop the world的次數
- Full GC的時間 = 老年代GC時 stop the world的總時間
- CMS 不等於Full GC,我們可以看到CMS分為多個階段,只有stop the world的階段被計算到了Full GC的次數和時間,而和業務線程並發的GC的次數和時間則不被認為是Full GC。CMS主要可以分為initial mark(stop the world), concurrent mark, remark(stop the world), concurrent sweep幾個階段,其中initial mark和remark會stop the world。
六、CMS缺點
- 垃圾回收時會占用一部分線程,導致系統變慢,總吞吐量會降低。
- 無法處理浮動垃圾,需要預留足夠的內存空間給用戶線程使用,可以通過 -XX:CMSInitiatingOccupancyFraction 參數控制觸發垃圾回收的閾值。
- 如果預留的內存無法滿足程序需要,就會出現“Concurrent Mode Failure”失敗,這時將啟動應急預案,啟用Serial Old 進行垃圾回收,停頓時間會變長。所以-XX:CMSInitiatingOccupancyFraction 參數的值設置的太高,會導致頻繁“Concurrent Mode Failure”失敗,性能反而降低。
- 標記-清理,容易產生內存碎片。-XX:+UseCMSCompactAtFullColletion 開啟碎片整理功能,默認開啟,-XX:CMSFullGCsBeforeCompaction,控制多少次不壓縮的FullGC之后來一次帶壓縮的
七、常見問題
1、promotion failed – concurrent mode failure
Minor GC后, Survivor空間容納不了剩余對象,將要放入老年代,老年代有碎片或者不能容納這些對象,就產生了concurrent mode failure, 然后進行stop-the-world的Serial Old收集器。
解決辦法:-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5 或者調大新生代或者Survivor空間
2、concurrent mode failure
CMS是和業務線程並發運行的,在執行CMS的過程中有業務對象需要在老年代直接分配,例如大對象,但是老年代沒有足夠的空間來分配,所以導致concurrent mode failure, 然后需要進行stop-the-world的Serial Old收集器。
解決辦法:+XX:CMSInitiatingOccupancyFraction,調大老年帶的空間,+XX:CMSMaxAbortablePrecleanTime
3、碎片整理
-XX:+UseCMSCompactAtFullCollection 強制進行空間碎片整理
CMS 采用標記算法,會產生大量的空間碎片。以上參數就是強制執行一次空間碎片整理,但是空間碎片整理會引發STW。
-XX:+CMSFullGCsBeforeCompaction 配置經過幾次的FullGC進行空間碎片整理
-XX:+CMSFullGCsBeforeCompaction=10 經過10次FGC后進行空間碎片整理,以降低STW次數
