JVM 經典垃圾收集器 —— CMS 收集器


本文部分摘自《深入理解 Java 虛擬機第三版》


概述

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。由於大部分 Java 應用主要集中在互聯網網站以及基於瀏覽器的 B/S 系統的服務端,這類應用通常會較爲關注服務的響應速度,希望系統的停頓時間儘可能少,CMS 收集器就非常符合這類應用的需求


步驟

從名字可以知道,CMS 收集器是基於標記 - 清除算法實現的,它的運作過程分爲四個步驟:

  1. 初始標記(CMS initial mark)

    僅僅只是標記一下 GC Roots 能直接關聯到的對象,速度很快,需要 Stop The World

  2. 併發標記(CMS concurrent mark)

    就是從 GC Roots 的直接關聯對象開始遍歷整個對象圖的過程,耗時較長,但不需要停頓用戶線程,可與垃圾收集器線程一起併發執行

  3. 重新標記(CMS remark)

    該階段是爲了修正併發標記期間,因用戶程序運作而導致標記產生變動的那一部分對象的標記記錄,這個階段需要 Stop The World,而且停頓時間通常比初始階段稍長一些,但也遠比並發標記階段的時間短

  4. 併發清除(CMS concurrent sweep)

    清理刪除掉標記階段判斷已經死亡的對象,由於不需要移動存活對象,所有這個階段可以和用戶線程併發執行

由於整個過程中耗時最長的是併發標記和併發清除階段,而這兩個階段都可以和用戶線程併發執行,所以從總體上看,CMS 收集器內存回收過程是與用戶線程一起併發執行的


CMS 收集器的不足

CMS 收集器的主要優點就是:併發收集、低停頓,因此也稱 CMS 收集器爲併發低停頓收集器。但 CMS 還遠未達到完美的程度,它至少有以下四個明顯的缺點:

  1. 對處理器資源非常敏感

    在併發階段,CMS 雖然不會導致用戶線程停頓,但卻會因爲佔用了一部分線程(或者說是處理器的計算能力)而導致應用程序變慢,降低吞吐量。處理器核心數在四個或以上那還好,如果不足四個,CMS 會佔用將近一半的運算能力去執行收集器線程,這將導致用戶程序的執行效率大幅降低

  2. 無法處理浮動垃圾

    在 CMS 的併發標記和併發清理階段,由於用戶線程繼續運行,因此有可能會有新的垃圾對象產生。但這一部分垃圾對象是出現在標記結束之後,CMS 無法在當次收集中處理掉它們,只能留待下一次垃圾收集在清理,這一部分垃圾就稱爲浮動垃圾

  3. 可能會出現併發失敗

    同樣也是由於垃圾收集階段用戶線程還需持續執行,那就必須預留足夠的內存空間供用戶線程使用。因此 CMS 收集器不能像其他收集器那樣等老年代幾乎完全填滿再進行收集,而必須預留一部分空間供併發收集時的程序運作使用,這部分空間的大小可以通過 -XX:CMSInitiatingOccu-pancyFraction 參數來設置。如果 CMS 運行期間預留的內存無法滿足程序分配對象的需要,就會出現一次併發失敗,這時虛擬機不得不啓用預備方案:凍結用戶線程,臨時啓用 Serial Old 收集器來重新進行老年代的垃圾收集,導致 Stop The World

  4. 大量空間碎片的產生

    CMS 是一款基於標記 - 清除算法實現的收集器,這也意味着收集結束時會產生大量空間碎片。爲了解決這個問題,CMS 收集器提供了一個 -XX:+UseCMS-CompactAtFullCollection 開關參數,用於在收集結束後做一次內存整理,以及 -XX:CMSFullGCsBefore-Compaction 參數,要求 CMS 收集器在執行若干次不整理空間的 Full GC 之後,下一次 Full GC 前先做一次碎片整理


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章