摘要:之前根據(jù)的內(nèi)存管理白皮書介紹了在分代算法中的幾個(gè)垃圾收集器,本文將介紹垃圾收集器。本節(jié)介紹的收集過程,收集器主要包括了以下種操作年輕代收集并發(fā)收集,和應(yīng)用線程同時(shí)執(zhí)行混合式垃圾收集必要時(shí)的接下來,我們進(jìn)行一一介紹。
之前根據(jù) Sun 的內(nèi)存管理白皮書介紹了在 HotSpot JVM 分代算法中的幾個(gè)垃圾收集器,本文將介紹 G1 垃圾收集器。
G1 的主要關(guān)注點(diǎn)在于達(dá)到可控的停頓時(shí)間,在這個(gè)基礎(chǔ)上盡可能提高吞吐量,這一點(diǎn)非常重要。
G1 被設(shè)計(jì)用來長期取代 CMS 收集器,和 CMS 相同的地方在于,它們都屬于并發(fā)收集器,在大部分的收集階段都不需要掛起應(yīng)用程序。區(qū)別在于,G1 沒有 CMS 的碎片化問題(或者說不那么嚴(yán)重),同時(shí)提供了更加可控的停頓時(shí)間。
如果你的應(yīng)用使用了較大的堆(如 6GB 及以上)而且還要求有較低的垃圾收集停頓時(shí)間(如 0.5 秒),那么 G1 是你絕佳的選擇,是時(shí)候放棄 CMS 了。
閱讀建議:本文力求用簡(jiǎn)單的話介紹清楚 G1 收集器,但是并不會(huì)重復(fù)介紹每一個(gè)細(xì)節(jié),所以希望讀者了解其他幾個(gè)收集器的工作過程,尤其是 CMS 收集器。
G1 總覽首先是內(nèi)存劃分上,之前介紹的分代收集器將整個(gè)堆分為年輕代、老年代和永久代,每個(gè)代的空間是確定的。
而 G1 將整個(gè)堆劃分為一個(gè)個(gè)大小相等的小塊(每一塊稱為一個(gè) region),每一塊的內(nèi)存是連續(xù)的。和分代算法一樣,G1 中每個(gè)塊也會(huì)充當(dāng) Eden、Survivor、Old 三種角色,但是它們不是固定的,這使得內(nèi)存使用更加地靈活。
執(zhí)行垃圾收集時(shí),和 CMS 一樣,G1 收集線程在標(biāo)記階段和應(yīng)用程序線程并發(fā)執(zhí)行,標(biāo)記結(jié)束后,G1 也就知道哪些區(qū)塊基本上是垃圾,存活對(duì)象極少,G1 會(huì)先從這些區(qū)塊下手,因?yàn)閺倪@些區(qū)塊能很快釋放得到很大的可用空間,這也是為什么 G1 被取名為 Garbage-First 的原因。
在 G1 中,目標(biāo)停頓時(shí)間非常非常重要,用 -XX:MaxGCPauseMillis=200 指定期望的停頓時(shí)間。
G1 使用了停頓預(yù)測(cè)模型來滿足用戶指定的停頓時(shí)間目標(biāo),并基于目標(biāo)來選擇進(jìn)行垃圾回收的區(qū)塊數(shù)量。G1 采用增量回收的方式,每次回收一些區(qū)塊,而不是整堆回收。
我們要知道 G1 不是一個(gè)實(shí)時(shí)收集器,它會(huì)盡力滿足我們的停頓時(shí)間要求,但也不是絕對(duì)的,它基于之前垃圾收集的數(shù)據(jù)統(tǒng)計(jì),估計(jì)出在用戶指定的停頓時(shí)間內(nèi)能收集多少個(gè)區(qū)塊。
注意:G1 有和應(yīng)用程序一起運(yùn)行的并發(fā)階段,也有 stop-the-world 的并行階段。但是,F(xiàn)ull GC 的時(shí)候還是單線程運(yùn)行的,所以我們應(yīng)該盡量避免發(fā)生 Full GC,后面我們也會(huì)介紹什么時(shí)候會(huì)觸發(fā) Full GC。
G1 內(nèi)存占用
注:這里不那么重要。
G1 比 ParallelOld 和 CMS 會(huì)需要更多的內(nèi)存消耗,那是因?yàn)橛胁糠謨?nèi)存消耗于簿記(accounting)上,如以下兩個(gè)數(shù)據(jù)結(jié)構(gòu):
Remembered Sets:每個(gè)區(qū)塊都有一個(gè) RSet,用于記錄進(jìn)入該區(qū)塊的對(duì)象引用(如區(qū)塊 A 中的對(duì)象引用了區(qū)塊 B,區(qū)塊 B 的 Rset 需要記錄這個(gè)信息),它用于實(shí)現(xiàn)收集過程的并行化以及使得區(qū)塊能進(jìn)行獨(dú)立收集。總體上 Remembered Sets 消耗的內(nèi)存小于 5%。
Collection Sets:將要被回收的區(qū)塊集合。GC 時(shí),在這些區(qū)塊中的對(duì)象會(huì)被復(fù)制到其他區(qū)塊中,總體上 Collection Sets 消耗的內(nèi)存小于 1%。
前面啰里啰嗦說了挺多的,唯一要記住的就是,G1 的設(shè)計(jì)目標(biāo)就是盡力滿足我們的目標(biāo)停頓時(shí)間上的要求。
本節(jié)介紹 G1 的收集過程,G1 收集器主要包括了以下 4 種操作:
1、年輕代收集
2、并發(fā)收集,和應(yīng)用線程同時(shí)執(zhí)行
3、混合式垃圾收集
*、必要時(shí)的 Full GC
接下來,我們進(jìn)行一一介紹。
首先,我們來看下 G1 的堆結(jié)構(gòu):
年輕代中的垃圾收集流程(Young GC):
我們可以看到,年輕代收集概念上和之前介紹的其他分代收集器大差不差的,但是它的年輕代會(huì)動(dòng)態(tài)調(diào)整。
接下來是 Old GC 的流程(含 Young GC 階段),其實(shí)把 Old GC 理解為并發(fā)周期是比較合理的,不要單純地認(rèn)為是清理老年代的區(qū)塊,因?yàn)檫@一步和年輕代收集也是相關(guān)的。下面我們介紹主要流程:
1.初始標(biāo)記:stop-the-world,它伴隨著一次普通的 Young GC 發(fā)生,然后對(duì) Survivor 區(qū)(root region)進(jìn)行標(biāo)記,因?yàn)樵搮^(qū)可能存在對(duì)老年代的引用。
因?yàn)?Young GC 是需要 stop-the-world 的,所以并發(fā)周期直接重用這個(gè)階段,雖然會(huì)增加 CPU 開銷,但是停頓時(shí)間只是增加了一小部分。
2.掃描根引用區(qū):掃描 Survivor 到老年代的引用,該階段必須在下一次 Young GC 發(fā)生前結(jié)束。
這個(gè)階段不能發(fā)生年輕代收集,如果中途 Eden 區(qū)真的滿了,也要等待這個(gè)階段結(jié)束才能進(jìn)行 Young GC。
3.并發(fā)標(biāo)記:尋找整個(gè)堆的存活對(duì)象,該階段可以被 Young GC 中斷。
這個(gè)階段是并發(fā)執(zhí)行的,中間可以發(fā)生多次 Young GC,Young GC 會(huì)中斷標(biāo)記過程
4.重新標(biāo)記:stop-the-world,完成最后的存活對(duì)象標(biāo)記。使用了比 CMS 收集器更加高效的 snapshot-at-the-beginning (SATB) 算法。
Oracle 的資料顯示,這個(gè)階段會(huì)回收完全空閑的區(qū)塊
5.清理:清理階段真正回收的內(nèi)存很少。
到這里,G1 的一個(gè)并發(fā)周期就算結(jié)束了,其實(shí)就是主要完成了垃圾定位的工作,定位出了哪些分區(qū)是垃圾最多的。
并發(fā)周期結(jié)束后是混合垃圾回收周期,不僅進(jìn)行年輕代垃圾收集,而且回收之前標(biāo)記出來的老年代的垃圾最多的部分區(qū)塊。
混合垃圾回收周期會(huì)持續(xù)進(jìn)行,直到幾乎所有的被標(biāo)記出來的分區(qū)(垃圾占比大的分區(qū))都得到回收,然后恢復(fù)到常規(guī)的年輕代垃圾收集,最終再次啟動(dòng)并發(fā)周期。
Full GC到這里我們已經(jīng)說了年輕代收集、并發(fā)周期、混合回收周期了,大家要熟悉這幾個(gè)階段的工作。
下面我們來介紹特殊情況,那就是會(huì)導(dǎo)致 Full GC 的情況,也是我們需要極力避免的:
1.concurrent mode failure:并發(fā)模式失敗,CMS 收集器也有同樣的概念。G1 并發(fā)標(biāo)記期間,如果在標(biāo)記結(jié)束前,老年代被填滿,G1 會(huì)放棄標(biāo)記。
這個(gè)時(shí)候說明堆需要增加了,或者需要調(diào)整并發(fā)周期,如增加并發(fā)標(biāo)記的線程數(shù)量,讓并發(fā)標(biāo)記盡快結(jié)束或者就是更早地進(jìn)行并發(fā)周期,默認(rèn)是整堆內(nèi)存的 45% 被占用就開始進(jìn)行并發(fā)周期。
2.晉升失敗:并發(fā)周期結(jié)束后,是混合垃圾回收周期,伴隨著年輕代垃圾收集,進(jìn)行清理老年代空間,如果這個(gè)時(shí)候清理的速度小于消耗的速度,導(dǎo)致老年代不夠用,那么會(huì)發(fā)生晉升失敗。
說明混合垃圾回收需要更迅速完成垃圾收集,也就是說在混合回收階段,每次年輕代的收集應(yīng)該處理更多的老年代已標(biāo)記區(qū)塊。
3.疏散失敗:年輕代垃圾收集的時(shí)候,如果 Survivor 和 Old 區(qū)沒有足夠的空間容納所有的存活對(duì)象。這種情況肯定是非常致命的,因?yàn)榛旧弦呀?jīng)沒有多少空間可以用了,這個(gè)時(shí)候會(huì)觸發(fā) Full GC 也是很合理的。
最簡(jiǎn)單的就是增加堆大小
4.大對(duì)象分配失敗,我們應(yīng)該盡可能地不創(chuàng)建大對(duì)象,尤其是大于一個(gè)區(qū)塊大小的那種對(duì)象。
簡(jiǎn)單小結(jié)看完上面的 Young GC 和 Old GC 等,很多讀者可能還是很懵的,這里說幾句不嚴(yán)謹(jǐn)?shù)陌自捨膸椭x者進(jìn)行理解:
首先,最好不要把上面的 Old GC 當(dāng)做是一次 GC 來看,而應(yīng)該當(dāng)做并發(fā)標(biāo)記周期來理解,雖然它確實(shí)會(huì)釋放出一些內(nèi)存。
并發(fā)標(biāo)記結(jié)束后,G1 也就知道了哪些區(qū)塊是最適合被回收的,那些完全空閑的區(qū)塊會(huì)在這這個(gè)階段被回收。如果這個(gè)階段釋放了足夠的內(nèi)存出來,其實(shí)也就可以認(rèn)為結(jié)束了一次 GC。
我們假設(shè)并發(fā)標(biāo)記結(jié)束了,那么下次 GC 的時(shí)候,還是會(huì)先回收年輕代,如果從年輕代中得到了足夠的內(nèi)存,那么結(jié)束;過了幾次后,年輕代垃圾收集不能滿足需要了,那么就需要利用之前并發(fā)標(biāo)記的結(jié)果,選擇一些活躍度最低的老年代區(qū)塊進(jìn)行回收。直到最后,老年代會(huì)進(jìn)入下一個(gè)并發(fā)周期。
那么什么時(shí)候會(huì)啟動(dòng)并發(fā)標(biāo)記周期呢?這個(gè)是通過參數(shù)控制的,下面馬上要介紹這個(gè)參數(shù)了,此參數(shù)默認(rèn)值是 45,也就是說當(dāng)堆空間使用了 45% 后,G1 就會(huì)進(jìn)入并發(fā)標(biāo)記周期。
G1 參數(shù)配置和最佳實(shí)踐G1 調(diào)優(yōu)的目標(biāo)是盡量避免出現(xiàn) Full GC,其實(shí)就是給老年代足夠的空間,或相對(duì)更多的空間。
有以下幾點(diǎn)我們可以進(jìn)行調(diào)整的方向:
增加堆大小,或調(diào)整老年代和年輕代的比例,這個(gè)很好理解
增加并發(fā)周期的線程數(shù)量,其實(shí)就是為了加快并發(fā)周期快點(diǎn)結(jié)束
讓并發(fā)周期盡早開始,這個(gè)是通過設(shè)置堆使用占比來調(diào)整的(默認(rèn) 45%)
在混合垃圾回收周期中回收更多的老年代區(qū)塊
G1 的很重要的目標(biāo)是達(dá)到可控的停頓時(shí)間,所以很多的行為都以這個(gè)目標(biāo)為出發(fā)點(diǎn)開展的。
我們通過設(shè)置 -XX:MaxGCPauseMillis=N 來指定停頓時(shí)間(單位 ms,默認(rèn) 200ms),如果沒有達(dá)到這個(gè)目標(biāo),G1 會(huì)通過各種方式來補(bǔ)救:調(diào)整年輕代和老年代的比例,調(diào)整堆大小,調(diào)整晉升的年齡閾值,調(diào)整混合垃圾回收周期中處理的老年代的區(qū)塊數(shù)量等等。
當(dāng)然了,調(diào)整每個(gè)參數(shù)滿足了一個(gè)條件的同時(shí)往往也會(huì)引入另一個(gè)問題,比如為了降低停頓時(shí)間,我們可以減小年輕代的大小,可是這樣的話就會(huì)增加年輕代垃圾收集的頻率。如果我們減少混合垃圾回收周期處理的老年代區(qū)塊數(shù)量,雖然可以更容易滿足停頓時(shí)間要求,可是這樣就會(huì)增加 Full GC 的風(fēng)險(xiǎn)等等。
下面介紹最常用也是最基礎(chǔ)的一些參數(shù)的設(shè)置,涉及到更高級(jí)的調(diào)優(yōu)參數(shù)設(shè)置,請(qǐng)讀者自行參閱其他資料。
參數(shù)介紹:
-XX:+UseG1GC 使用 G1 收集器
-XX:MaxGCPauseMillis=200 指定目標(biāo)停頓時(shí)間,默認(rèn)值 200 毫秒。
在設(shè)置 -XX:MaxGCPauseMillis 值的時(shí)候,不要指定為平均時(shí)間,而應(yīng)該指定為滿足 90% 的停頓在這個(gè)時(shí)間之內(nèi)。記住,停頓時(shí)間目標(biāo)是我們的目標(biāo),不是每次都一定能滿足的。
-XX:InitiatingHeapOccupancyPercent=45 整堆使用達(dá)到這個(gè)比例后,觸發(fā)并發(fā) GC 周期,默認(rèn) 45%。
如果要降低晉升失敗的話,通常可以調(diào)整這個(gè)數(shù)值,使得并發(fā)周期提前進(jìn)行
-XX:NewRatio=n
老年代/年輕代,默認(rèn)值 2,即 1/3 的年輕代,2/3 的老年代老年代/年輕代,默認(rèn)值 2,即 1/3 的年輕代,2/3 的老年代。不要設(shè)置年輕代為固定大小,否則:G1 不再需要滿足我們的停頓時(shí)間目標(biāo),不能再按需擴(kuò)容或縮容年輕代大小
-XX:SurvivorRatio=n
Eden/Survivor,默認(rèn)值 8,這個(gè)和其他分代收集器是一樣的
-XX:MaxTenuringThreshold =n
從年輕代晉升到老年代的年齡閾值,也是和其他分代收集器一樣的
-XX:ParallelGCThreads=n
并行收集時(shí)候的垃圾收集線程數(shù)
-XX:ConcGCThreads=n
并發(fā)標(biāo)記階段的垃圾收集線程數(shù),增加這個(gè)值可以讓并發(fā)標(biāo)記更快完成,如果沒有指定這個(gè)值,JVM 會(huì)通過以下公式計(jì)算得到ConcGCThreads=(ParallelGCThreads + 2) / 4^3
-XX:G1ReservePercent=n
堆內(nèi)存的預(yù)留空間百分比,默認(rèn) 10,用于降低晉升失敗的風(fēng)險(xiǎn),即默認(rèn)地會(huì)將 10% 的堆內(nèi)存預(yù)留下來。
-XX:G1HeapRegionSize=n
每一個(gè) region 的大小,默認(rèn)值為根據(jù)堆大小計(jì)算出來,取值 1MB~32MB,這個(gè)我們通常指定整堆大小就好了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/72449.html
摘要:表示允許垃圾收集線程處理本次垃圾收集開始前沒有處理好的日志緩沖區(qū),這可以確保當(dāng)前分區(qū)的是最新的。垃圾收集線程在完成其他任務(wù)的時(shí)間展示每個(gè)垃圾收集線程的最小最大平均差值和總共時(shí)間。 本文翻譯自:https://www.redhat.com/en/blog/collecting-and-reading-g1-garbage-collector-logs-part-2?source=auth...
摘要:虛擬機(jī)所處的區(qū)域,則表示它是屬于新生代收集器還是老年代收集器。虛擬機(jī)總共運(yùn)行了分鐘,其中垃圾收集花掉分鐘,那么吞吐量就是。收集器線程所占用的數(shù)量為。 本文主要從GC(垃圾回收)的角度試著對(duì)jvm中的內(nèi)存分配策略與相應(yīng)的垃圾收集器做一個(gè)介紹。 注:還是老規(guī)矩,本著能畫圖就不BB原則,盡量將各知識(shí)點(diǎn)通過思維導(dǎo)圖或者其他模型圖的方式進(jìn)行說明。文字僅記錄額外的思考與心得,以及其他特殊情況 內(nèi)存...
摘要:深入理解虛擬機(jī)高級(jí)特性與最佳實(shí)踐第二版讀書筆記與常見面試題總結(jié)上篇文章傳送門深入理解虛擬機(jī)之內(nèi)存區(qū)域本節(jié)常見面試題推薦帶著問題閱讀,問題答案在文中都有提到如何判斷對(duì)象是否死亡兩種方法。虛引用主要用來跟蹤對(duì)象被垃圾回收的活動(dòng)。 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第二版》讀書筆記與常見面試題總結(jié) 上篇文章傳送門: 深入理解虛擬機(jī)之Java內(nèi)存區(qū)域 本節(jié)常見面試題(推薦帶著...
摘要:之前的堆內(nèi)存示意圖從上圖可以看出堆內(nèi)存的分為新生代老年代和永久代。對(duì)象優(yōu)先在區(qū)分配目前主流的垃圾收集器都會(huì)采用分代回收算法,因此需要將堆內(nèi)存分為新生代和老年代,這樣我們就可以根據(jù)各個(gè)年代的特點(diǎn)選擇合適的垃圾收集算法。 上文回顧:《可能是把Java內(nèi)存區(qū)域講的最清楚的一篇文章》 寫在前面 本節(jié)常見面試題: 問題答案在文中都有提到 如何判斷對(duì)象是否死亡(兩種方法)。 簡(jiǎn)單的介紹一下強(qiáng)引用...
摘要:此外,從結(jié)果我們可以得知,一個(gè)堆對(duì)象的放在局部變量表中的第一項(xiàng)引用會(huì)永遠(yuǎn)存在,在方法體內(nèi)可以將引用賦值給其他變量,這樣堆中對(duì)象就可以被其他變量所引用,即不會(huì)被回收。 原創(chuàng)不易,如需轉(zhuǎn)載,請(qǐng)注明出處https://www.cnblogs.com/baixianlong/p/10697554.html,多多支持哈! 一、什么是GC? GC是垃圾收集的意思,內(nèi)存處理是編程人員容易出現(xiàn)問題的地...
閱讀 2975·2021-11-24 10:22
閱讀 3045·2021-11-23 10:10
閱讀 1353·2021-09-28 09:35
閱讀 1752·2019-08-29 13:16
閱讀 1395·2019-08-26 13:29
閱讀 2782·2019-08-26 10:27
閱讀 678·2019-08-26 10:09
閱讀 1436·2019-08-23 18:05