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



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


CMS 收集器

1. 概述

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

2. 步驟

從名字可以知道,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 收集器內存回收過程是與用戶線程一起並發執行的

3. 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 前先做一次碎片整理


Garbage First 收集器

1. 概述

Garbage First(G1)收集器是一款主要面向服務端應用的垃圾收集器,開創了收集器面向局部收集的設計思路和基於 Region 的內存布局形式。HotSpot 開發團隊對 G1 收集器的期望就是能在將來替代 CMS 收集器,所以在 JDK9 發布之日,G1 便宣告取代 Parallel Scavenge 加 Parallel Old 組合,成為服務端模式下的默認垃圾收集器,而 CMS 則淪為不推薦使用

2. 分區概念

在過去,包括 CMS 在內,垃圾收集的范圍要么是整個新生代,要么是整個老年代,再要么是整個 Java 堆。而 G1 可以面向堆內存任何部分來組成回收集(Collection Set,一般簡稱 CSet)進行回收,衡量標准是哪塊內存中垃圾數量最多,回收收益最大,這就是 G1 收集器的 Mixed GC 模式

雖然 G1 也是基於分代收集理論設計,但其對內存布局與其他收集器有明顯差異。G1 把連續的 Java 堆划分成多個大小相等的獨立區域(Region),每一個 Region 可以根據需要扮演新生代的 Eden 空間、Survivor 空間、老年代空間等等。收集器能對扮演不同角色的 Region 采用不同的策略處理

Region 中還有一類特殊的 Humongous 區域,專門用來存儲大對象。只要該對象大小超過一半的 Region 的容量即可判定為大對象。而對於那些超過整個 Region 容量的超級大對象,將會被存放在 N 個連續的 Humongous Region 之中,G1 的大多數行為都把 Humongous Region 作為老年代的一部分來看待

3. 停頓時間模型

停頓時間模型的意思是能夠支持指定在一個長度為 M 毫秒的時間片段內,消耗在垃圾收集上的時間大概率不超過 M 毫秒這么一個目標。G1 收集器作為 CMS 收集器的替代者,自然可以實現這個目標

G1 之所以能建立起可預測的停頓時間模型,是因為它將 Region 作為單詞回收的最小單元,即每次收集到的內存空間都是 Region 大小的整數倍,這樣可以有計划地避免進行全區域的垃圾收集。G1 收集器還可以跟蹤每個 Region 的垃圾堆積的“價值”大小,即回收所獲得的空間大小以及所需時間,並在后台維護一個優先級列表,每次根據用戶設置的允許收集停頓時間(使用 -XX:MaxGCPauseMillis 指定),優先處理回收價值最大的 Region。這種使用 Region 划分內存空間,以及具有優先級的區域回收方式,保證了 G1 收集器在有限的時間內獲取盡可能高的收集效率

4. 要解決的難點

G1 收集器的設計理念看似無太多驚人之處,其實有很多關鍵的細節問題需要解決:

  • 如何解決跨 Region 引用對象?

    這個問題的解決思路可以使用之前提到過的記憶集來處理,但由於每個 Region 都要維護自己的記憶集,因此實現更加復雜,而且內存占用負擔也更重

  • 並發標記階段如何保證收集線程與用戶線程互不干擾?

    對應該問題,CMS 采用增量更新算法解決,而 G1 采用原始快照算法解題。另外,G1 還為每一個 Region 設計了兩個名為 TAMS(Top At Mark Start)的指針,用於在並發回收過程中新對象的內存分配。G1 收集器默認在這個地址以上的對象是存活的,不會納入回收范圍

  • 如何建立起可靠的停頓預測模型?

    G1 收集器的停頓時間模型是以衰減均值(Decaying Average)為理論基礎實現的。衰減均值是指它會比普通的平均值更容易受新數據影響,因此,Region 的統計狀態越新,越能決定其回收的價值

5. 步驟

G1 收集器的運作過程大致可划分為以下四個步驟:

  1. 初始標記

    僅僅標記一下 GC Roots 能直接關聯的對象,並修改 TAMS 指針的值。該階段需停頓線程,但耗時很短,而且是借進行 Minor GC 時同步完成的,實際上並沒有額外的停頓

  2. 並發標記

    從 GC Roots 開始對堆中對象進行可達性分析,找出要回收對象。該階段耗時較長,但可與用戶程序並發執行。當掃描完成后,還要重新處理 SATB 記錄下在並發時有引用變動的對象

  3. 最終標記

    用戶線程短暫暫停,處理並發階段結束后遺留下來的少量 SATB 記錄

  4. 篩選回收

    更新 Region 統計數據,對各個 Region 的回收價值和成本進行排序,根據用戶所期望的停頓時間制定回收計划,然后把要回收的那一部分 Region 的存活對象復制到空的 Region 中,再清理掉整個舊 Region 的全部空間。這里涉及到存活對象的移動,必須暫停用戶線程

6. G1 和 CMS 的對比

G1 和 CMS 都非常關注停頓時間控制,毫無疑問,可以由用戶指定期望的停頓時間是 G1 收集器的一大殺手鐧。G1 收集器經常被拿來和 CMS 收集器比較,從長遠來看,G1 收集器肯定是會取代 CMS 收集器的

除了更先進的設計理念,單從傳統的算法理論來看,G1 從整體來看是基於標記 - 整理算法實現,而從局部來看(兩個 Region 之間)又是基於標記 - 復制算法實現,這意味着 G1 不會產生內存碎片。但 G1 並非全方面碾壓 CMS,G1 由於其復雜的內部細節實現,使得垃圾收集時的內存占用和程序運行時的額外執行負載都要比 CMS 高。使用哪款收集器,往往要針對具體場景才能做定量比較,目前在小內存應用上 CMS 的表現大概率會優於 G1,而在大內存應用上 G1 則占有優勢,這個平衡點通常在 6GB ~ 8GB 之間。當然,隨着 HotSpot 開發者對 G1 的持續優化,最終勝利的天平必定回向 G1 傾斜



免責聲明!

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



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