這三大特性,讓 G1 取代了 CMS!
大家好,我是樹哥。
之前我們聊過 CMS 回收器,但那時候我們說 CMS 回收器已經落伍了,現在應該是用 G1 回收器的時候了。那麼 G1 回收器到底有什麼魔力,它比 CMS 回收器相比強在哪裡呢?今天,就讓樹哥帶大家盤一盤!
G1 回收器的歷史
G1(Garbage-First)回收器早在 JDK1.7 的時候就確定要做,但直到 JDK7u4 的時候才正式推出使用。等到了 JDK9 之後變成了默認的垃圾回收器,同時廢棄了 CMS 回收器。
G1 回收器特性
G1 回收器是一款面向服務端應用的垃圾回收器,它的長期實名是替換 CMS 回收器。
G1 回收器於 CMS 回收器相比,它們有相似的地方,例如:都是關注 GC 停頓時間的回收器,都採用了分代回收的思想。但從整體的實現上來看,G1 回收器做了非常多的改進,可以說是對 CMS 回收器的全面改進。相對於 CMS 回收器來說,G1 回收器有下面幾個不同的地方:
- 採用化整為零的分區思想
- 採用標記-整理的垃圾回收演算法
- 可預測的 GC 停頓時間
分區思想
對於 CMS 及之前的回收器來說,其 JVM 記憶體空間按照分代的思路劃分成物理連續的一大片區域,如下圖所示。
但在 G1 回收器中,雖然也採用了分代的思路,但其並沒有為其分配一塊連續的記憶體,而是將整塊記憶體化整為零拆分成一個個 Region,如下圖所示。
正如上圖所示,G1 回收器不再為年輕代和老年代劃分大塊的記憶體,而是劃分成了一個個的 Region,每個 Region 被標記成年輕代或者老年代。在 G1 中,還多了一個 Humongous 區域,其是為了優化大對象的分配而誕生的。
G1 回收器化整為零的 Region 設計思想,是 G1 回收器比 CMS 回收器強大的核心。 通過將大塊的記憶體化整為零,G1 回收器能夠更加靈活地控制 GC 停頓時間,並且也解決了 CMS 回收器存在的記憶體碎片問題以及大記憶體下的長 GC 停頓時間問題。
標記-整理演算法
G1 回收器與 CMS 回收器的另一個很大的區別是:G1 回收器使用的是「標記-整理」演算法,而 CMS 回收器使用的是「標記-清除」演算法。 因此,CMS 回收器會產生非常多的記憶體碎片,而 G1 回收器則沒有這個困擾。
有些小夥伴會問:那為什麼 CMS 回收器不用「標記-整理」演算法呢?
很簡單,因為 CMS 回收器的老年代很大,使用「標記-整理」演算法需要耗費很長的 GC 停頓時間,這會導致介面響應時間變長。實際上 CMS 回收器後續提供了 -XX:+UseCMSCompactAtFullCollection
參數去實現記憶體壓縮,但在記憶體壓縮的時候 GC 停頓時間會很長,從而導致介面響應時間變長。
好奇寶寶又問了:G1 回收器也用的是「標記-整理」演算法,為啥就不會導致長 GC 停頓時間呢?
很簡單,因為 G1 回收器使用了分 Region 的思想,其將大塊的記憶體化整為零成為 Region。此外,其還維護了一個待回收 Region 列表,可以選擇回收性價比最高的 Region 進行回收,從而實現對 GC 停頓時間的靈活控制。
看到了么,G1 回收器化整為零的 Region 設計思想,真的是 G1 回收器的大殺器!
可預測的停頓時間
G1 回收器對於 CMS 而言還有一個很大的優勢,即能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度為 M 毫秒的時間片段內,消耗在垃圾收集上的時間不得超過 N 毫秒。對於該特性現在還用得比較少,大家了解一下就可以了。
垃圾回收過程
比起 CMS 回收器來說,G1 回收器的垃圾回收過程就比較特別了,其採用了「年輕代收集」和「混合收集」兩種垃圾回收方式。
年輕代收集
在應用剛剛啟動的時候,流量慢慢進來,JVM 開始生成對象。G1 會選擇一個分區並指定 eden 分區,當這塊分區用滿之後,G1 會選一個新的分區作為 eden 分區。這個操作會一直進行下去,一直到達到 eden 分區的上限,接著觸發一次年輕代收集。
年代收集採用的是「複製演算法」,其首先使用單 eden、雙 survivor 遷移存活對象。在遷移過程中,會根據對象年齡以及其他特性,將對象晉陞到老年代分區中,原有的年輕代分區會被整個回收掉。這個過程涉及到的規則和 CMS 回收器類似,只是 G1 回收器將記憶體化整為零了而已。
混合收集
隨著時間推移,越來越多的對象晉陞到老年代中。當老年代佔比(占 Java 堆記憶體的比例)達到 InitiatingHeapOccupancyPercent 參數之後,JVM 便會觸發「混合收集」進行垃圾收集。要注意的是:混合收集會收集年輕代和部分老年代的記憶體,其並不等同於 Full GC。Full GC 會回收整個老年代記憶體。
對於混合收集方式來說,其收集過程可以分為 4 個階段:
- 初始標記
- 並發標記
- 最終標記
- 篩選回收
初始標記。 該階段與 CMS 回收器一樣,都只是簡單標記一下 GC Roots 能直接關聯到的對象,讓後續 GC 回收執行緒能與用戶執行緒並發執行。初始標記階段是需要「Stop the World」的。
並發標記。 該階段與 CMS 回收器一樣,它從 GC Root 開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時很長,但可與用戶程式並發執行,不需要「Stop the World」。
最終標記。 該階段與 CMS 回收器一樣,它是為了修正在並發標記期間因用戶程式繼續運作而導致引用發生變化的問題。只是 G1 回收器採用了不同的方式去實現,在這個階段是需要「Stop the World」的。
篩選回收。 該階段與 CMS 回收器的並發清除一樣,它是去將標記為垃圾的對象清除掉。只是對於 G1 回收器來說,它會維護各個 Region 的回收價值和成本,隨後根據用戶期望的 GC 停頓時間來指定回收計劃。
整體看下來,我們會發現 G1 回收器的混合收集過程與 CMS 回收器非常類似,都經歷初始標記、並發標記、最終標記、篩選回收(並發清除)幾個階段。
總結
從 JDK7 正式推出到 JDK9 成為默認的垃圾收集器,G1 回收器用了兩代人的時間打敗了 CMS 回收器。
從 G1 回收器的實現來看,其開創性的化整為零的 Region 設計思想,無疑是其打敗 CMS 回收器的秘訣。通過該設計思想,G1 回收器得以更加靈活地控制 GC 停頓時間,同時也可以實現更加高效、複雜的功能,例如:根據回收空間和耗時選擇最佳的回收 Region、預測 GC 停頓時間等。
參考資料
- 名字解釋不錯!VIP!搞懂 G1 垃圾收集器 – GrimMjx – 部落格園
- 關於 GC 過程,寫得不錯!VIP!Java Hotspot G1 GC 的一些關鍵技術 – 美團技術團隊
- 08 大廠面試題:有了 G1 還需要其他垃圾回收器嗎?.md
- 官方資料!VIP!Garbage First Garbage Collector Tuning | Oracle 中國
- 官方資料!VIP!垃圾回收期的推薦使用場景!Java HotSpot Garbage Collection
- 還行!VIP!5 張圖帶你徹底理解 G1 垃圾收集器 – 51CTO.COM
- G1 垃圾收集器詳解 – 掘金
- 深入理解 Java 虛擬機系列 –12 垃圾回收篇 03 — 常用的垃圾回收器詳解 – 掘金
- 深入理解 JAVA 垃圾收集器 CMS,G1 工作流程原理 – 掘金
- GC – Java 垃圾回收器之G1詳解 | Java 全棧知識體系
- VIP!有美團的具體實踐!GC – Java 垃圾回收器之ZGC詳解 | Java 全棧知識體系
- 《深入理解 Java 虛擬機》
- CMS 垃圾回收器存在的問題及解決方案 – 程式碼先鋒網