探索G1垃圾回收器

前言

最近王子因為個人原因有些忙碌,導(dǎo)致文章更新比較慢,希望大家理解,之后也會持續(xù)和小伙伴們一起共同分享技術(shù)干貨。

上篇JVM的文章中我們對ParNew和CMS垃圾回收器已經(jīng)有了一個比較透徹的認(rèn)識,感興趣的小伙伴可以去回看一下探索ParNew和CMS垃圾回收器。

今天我們繼續(xù)探索垃圾回收器G1的原理,讓我們開始吧!

G1的內(nèi)存模型

G1是從jdk7開始出現(xiàn)的,在jdk9中被設(shè)為默認(rèn)垃圾收集器,目標(biāo)就是徹底替換掉CMS,那么為什么它可以替換掉CMS呢?

首先我們就來看看它的內(nèi)存模型吧。

其實G1是可以同時回收年輕代和老年代的,他最大的特點就是把jvm堆內(nèi)存拆分為了多個大小相等的Region,那么還存在年輕代和老年代嗎?

答案是肯定的,不同的是新生代可能包含了某些Region,老年代也可能包含了某些Region,如下圖:

到底有多少Region?每個Region有多大呢?

其實這個默認(rèn)情況下是自動計算的,假如我們給定整個堆內(nèi)存大小為4096M,然后使用“-XX:+UseG1GC”指定垃圾回收器為G1,此時會自動用堆內(nèi)存大小除以2048,因為JVM最多可以有2048個Region,然后Region的大小必須是2的倍數(shù)。

堆內(nèi)存為4096M,就會分配給每個Region 2M的內(nèi)存空間。我們使用G1默認(rèn)的計算方式就可以了。

當(dāng)然也可以通過參數(shù)“-XX:G1HeapRegionSize”來指定Region的大小。

新生代和老年代的默認(rèn)比例是多少呢?

我們知道使用ParNew和CMS垃圾回收器時,新生代和老年代的默認(rèn)比例是1:2,而使用G1后,默認(rèn)新生代對堆內(nèi)存的初始占比是5%,這個可以通過“-XX:G1NewSizePercent”來設(shè)置初始占比,一般不需要設(shè)置。

細(xì)心的小伙伴會發(fā)現(xiàn),這里說的占比是初始占比,因為系統(tǒng)運(yùn)行的時候,JVM其實會不停的給新生代增加更多的Region,但是最多新生代的占比不會超過60%,可以通過“-XX:G1MaxNewSizePercent”來設(shè)置。

而一旦發(fā)生了垃圾回收,新生代的Region數(shù)量還會減少,所以其實新生代和老年代的占比不是一成不變的,而是動態(tài)改變的。

新生代還有eden和survivor嗎?

答案是肯定的,新生代還是有eden和survivor的,只不過內(nèi)存占用會隨著Region的增多而增大。

G1的停頓時間控制

除了內(nèi)存的變化,G1還有一個最大的變化,就是可以讓我們設(shè)置一個垃圾回收的預(yù)期停頓時間,也就是說我們可以指定G1垃圾回收導(dǎo)致“Stop the World”的最長時間。

我們知道JVM一大痛點就是"Stop the World",盡量減少它的時間就可以做到JVM的優(yōu)化。

引入G1后,我們可以自己去設(shè)定這個停頓的最長時間了,相當(dāng)于直接控制了垃圾回收的性能。

G1要做到這一點就要去追蹤每個Region的回收價值,那什么是回收價值呢?大家看下圖:

比如兩個Region中,其中一個有10M的垃圾對象,垃圾回收需要耗時1s,另一個有20M的垃圾對象,垃圾回收耗時200ms。

然后G1進(jìn)行垃圾回收的時候,發(fā)現(xiàn)最近1小時垃圾回收已經(jīng)導(dǎo)致了幾百毫秒的系統(tǒng)停頓了,所以會選擇回收價值高的Region進(jìn)行回收,200ms的時間就能回收掉20M的垃圾對象,回收價值相對較高,所以會選擇這個Region進(jìn)行回收

G1控制停頓時間的思路,簡單來講就是,它會通過跟蹤Region的回收價值,盡可能的保證系統(tǒng)停頓時間在你設(shè)定的停頓時間范圍內(nèi)。

G1的垃圾回收詳解

上文我們了解到新生代還是有eden和survivor的,那么隨著新生代占據(jù)堆內(nèi)存大小的60%的時候,這個時候就會觸發(fā)新生代的GC,G1也會使用之前我們說過的復(fù)制算法進(jìn)行垃圾回收,進(jìn)入一個“Stop the World”狀態(tài)。

但是這個過程與之前的Minor GC其實是有差別的,首先回收的對象變成了帶有垃圾對象的Region,然后回收的同時會根據(jù)設(shè)定的停頓時間進(jìn)行價值回收,如上文所述。

什么時候進(jìn)入老年代呢?

這個可以說和之前是一模一樣的,簡單介紹如下:

新生代躲過多次垃圾回收后會進(jìn)入老年代;

GC后存活對象超過Survivor區(qū)的50%,那么會觸發(fā)動態(tài)年齡判定規(guī)則,符合規(guī)則的進(jìn)入老年代。

具體細(xì)節(jié)不在說明,可以參考王子之前的文章秒懂JVM的垃圾回收機(jī)制,有詳細(xì)解釋。

需要注意的是,G1的大對象不是存到老年代中的,而是提供了專門的Region來存放大對象。

在G1中,大對象的判斷規(guī)則就是這個對象超過了一個Region大小的50%,比如Region是2M的,那如果你的對象超過了1M,就會被認(rèn)定為大對象,做特殊處理。

而且如果這個大對象過大,可以橫跨多個Region進(jìn)行存儲,如下圖:

老年代具體又是怎么進(jìn)行垃圾回收的呢?

這個過程說起來可能稍微復(fù)雜了一點,但是它和CMS的垃圾回收過程其實是類似的。關(guān)于CMS的垃圾回收的幾個階段可以回顧王子的上篇文章探索ParNew和CMS垃圾回收器

首先我們要弄明白,什么時候會觸發(fā)新生代和老年代的混合垃圾回收?

G1有一個參數(shù)“-XX:InitiatingHeapOccupancyPercent”,默認(rèn)值為45%。

什么意思呢?就是說當(dāng)老年代占據(jù)了堆內(nèi)存的45%的Region的時候,就會觸發(fā)混合垃圾回收。

具體流程是什么樣的呢?

首先會觸發(fā)一次初始標(biāo)記操作,這個過程是要“Stop the World”的,對應(yīng)的就是CMS的初始標(biāo)記階段,細(xì)節(jié)不再說明。

接著會進(jìn)入并發(fā)標(biāo)記階段,這個階段同樣對應(yīng)CMS的并發(fā)標(biāo)記階段,不再說明。

接著會進(jìn)入最終標(biāo)記階段,這個階段其實和CMS的重新標(biāo)記階段也基本一致。

最后就是混合回收階段,這個階段和CMS的并發(fā)清理階段就不太一樣了,這個階段會計算每個Region中的存活對象數(shù)量,存活數(shù)量占比,還有執(zhí)行垃圾回收的耗時等問題。

接著會進(jìn)入“Stop the World”階段,然后全力以赴進(jìn)行垃圾回收,并盡量保證停止時間不超過我們設(shè)定好的時間,所以可能只會回收掉之前標(biāo)記好的一部分垃圾對象。

為什么要叫做混合回收呢,因為它不僅僅回收的是老年代,新生代和大對象的Region也會同時進(jìn)行回收,而具體回收哪些Region就要視情況而定了,根據(jù)價值回收價值G1會自己做出選擇。

而混合回收是可以進(jìn)行多次的,比如先停止系統(tǒng),混合回收掉一部分Region,再停止系統(tǒng),再執(zhí)行一次混合回收。

有參數(shù)可以控制這個數(shù)量,“-XX:G1MixedGCCountTarget”參數(shù),就是在一次混合回收的過程中,最后一個階段執(zhí)行幾次,默認(rèn)是8次。

為什么要這樣反復(fù)多次的回收呢?

因為這樣每次回收停止系統(tǒng)的時間都很短,在回收的間隙系統(tǒng)是可以正常運(yùn)行的。

還有個參數(shù)“-XX:G1HeapWastePercent”,默認(rèn)值是5%。

它的意思是,混合回收的時候,都是基于復(fù)制算法進(jìn)行的,把Region存活的對象放入其他Region,然后清除掉本來的Region。那么當(dāng)空閑的Region數(shù)量達(dá)到堆內(nèi)存的5%,就會立即停止混合回收。

而通過這種復(fù)制算法回收,也不會出現(xiàn)像CMS標(biāo)記清理算法導(dǎo)致的內(nèi)存碎片問題。

還有個參數(shù)“-XX:G1MixedGCLiveThresholdPercent”,默認(rèn)值是85%,意思就是回收Region的時候,存活的對象必須少于85%才可以被回收掉。否則存活對象太多,復(fù)制的時候成本是很高的。

如果回收失敗怎么辦?

如果在復(fù)制的時候發(fā)現(xiàn)沒有空閑的Region可以承載存活的對象,那么會觸發(fā)失敗,立馬停止系統(tǒng)進(jìn)程,采用單線程進(jìn)行標(biāo)記、清理和壓縮整理,空閑出一批Region,這個過程是極慢的。

總結(jié)

本文我們對G1的內(nèi)存機(jī)制和垃圾回收的算法做了一個比較清晰的解釋。

閱讀完本文,相信小伙伴們自己可以總結(jié)出G1和CMS究竟有什么不一樣了吧。

歡迎小伙伴們留言區(qū)討論G1和CMS的區(qū)別,王子會第一時間回復(fù)。

那我們下篇文章再見。

往期文章推薦:

大白話談JVM的類加載機(jī)制

JVM內(nèi)存模型不再是秘密

輕松理解JVM的分代模型

秒懂JVM的垃圾回收機(jī)制

探索ParNew和CMS垃圾回收器

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容