JVM G1(Garbage-First Garbage Collector)收集器全過程剖析

G1垃圾收集器的設(shè)計原則是“首先收集盡可能多的垃圾(Garbage First)”,目標(biāo)是為了盡量縮短處理超大堆(超過4GB)產(chǎn)生的停頓。

因此榄审,G1并不會等內(nèi)存耗盡(比如Serial 串行收集器驳概、Parallel并行收集器 )者快耗盡(CMS)的時候才開始垃圾回收,而是在內(nèi)部采用了啟發(fā)式算法旅薄,在老年代中找出具有高收集收益的分區(qū)(Region)進(jìn)行收集贡歧。

同時 G1 可以根據(jù)用戶設(shè)置的STW(Stop-The-World)停頓時間目標(biāo)(響應(yīng)時間優(yōu)先)自動調(diào)整年輕代和總堆的大小,停頓時間越短年輕代空間就可能越小赋秀,總堆空間越大利朵。

G1相對于CMS一個比較明顯的優(yōu)勢是,內(nèi)存碎片的產(chǎn)生率大大降低猎莲。

G1在 JDK7u4以上都可以使用绍弟,在JDK9開始,G1為默認(rèn)的垃圾收集器著洼,以替代CMS樟遣。

G1算法

算法:三色標(biāo)記 + SATB

G1的特性

  • 面向服務(wù)端應(yīng)用的垃圾收集器
  • 并行與并發(fā):G1能充分利用多CPU、多核環(huán)境使用多個CPU或CPU核心來縮短STW(Stop-The-World)停頓時間身笤。
  • 分代收集:G1物理上不分代豹悬,但邏輯上仍然有分代的概念。
  • 空間整合:不會產(chǎn)生內(nèi)存空間碎片液荸,收集后可提供規(guī)整的可用內(nèi)存瞻佛,整理空閑空間更快。
  • 可預(yù)測的停頓(它可以有計劃的避免在整個JAVA堆中進(jìn)行全區(qū)域的垃圾收集)
  • 適用于不需要實現(xiàn)很高吞吐量的場景
  • JAVA堆內(nèi)存布局與其它收集器存在很大差別,它將整個JAVA堆劃分為多個大小相等的獨立區(qū)域或分區(qū)(Region)伤柄。
  • G1收集器中绊困,虛擬機(jī)使用Remembered Set來避免全堆掃描。

G1的內(nèi)存模型

分區(qū)概念

傳統(tǒng)的GC收集器將連續(xù)的內(nèi)存空間劃分為新生代适刀、老年代和永久代(JDK 8去除了永久代秤朗,引入了元空間Metaspace),

這種劃分的特點是各代的存儲地址(邏輯地址笔喉,下同)是連續(xù)的取视。如下圖所示:

而G1的各代存儲地址是不連續(xù)的,每一代都使用了n個不連續(xù)的大小相同的Region常挚,每個Region占有一塊連續(xù)的虛擬內(nèi)存地址贫途。

Region (區(qū)域,分區(qū))

G1采用了分區(qū)(Region)的思路待侵,將整個堆空間分成若干個大小相等的內(nèi)存區(qū)域丢早,每次分配對象空間將逐段地使用內(nèi)存。

雖然還保留了新生代和老年代的概念秧倾,但新生代和老年代不再是物理隔離怨酝,它們都是一部分Region(不需要連續(xù))的集合。

因此那先,在堆的使用上农猬,G1并不要求對象的存儲一定是物理上連續(xù)的,只要邏輯上連續(xù)即可售淡;

每個分區(qū)Region也不會確定地為某個代服務(wù)斤葱,可以按需在年輕代和老年代之間切換。

啟動時可以通過參數(shù)-XX:G1HeapRegionSize=n可指定分區(qū)大小(1MB~32MB揖闸,且必須是2的冪)揍堕,默認(rèn)將整堆劃分為2048個分區(qū)。

Card (卡片)

在每個分區(qū)Region 內(nèi)部又被分成了若干個大小為512 Byte卡片(Card)汤纸,標(biāo)識堆內(nèi)存最小可用粒度衩茸。

所有分區(qū)Region 的卡片將會記錄在全局卡片表(Global Card Table)中。

分配的對象會占用物理上連續(xù)的若干個卡片贮泞。

當(dāng)查找對分區(qū)Region 內(nèi)對象的引用時便可通過記錄卡片來查找該引用對象(見RSet)楞慈。

每次對內(nèi)存的回收,都是對指定分區(qū)的卡片進(jìn)行處理啃擦。

Heap (堆)

G1同樣可以通過-Xms/-Xmx來指定堆空間大小囊蓝。

當(dāng)發(fā)生年輕代收集(YGC)或混合收集(Mixed GC)時,通過計算GC與應(yīng)用的耗費時間比令蛉,自動調(diào)整堆空間大小聚霜。

如果GC頻率太高,則通過增加堆尺寸,來減少GC頻率俯萎,相應(yīng)地GC占用的時間也隨之降低;

目標(biāo)參數(shù)-XX:GCTimeRatio即為GC與應(yīng)用的耗費時間比运杭,G1默認(rèn)為12(JDK7,8為99夫啊,JDK11+開始為12),而CMS默認(rèn)為99辆憔,因為CMS的設(shè)計原則是耗費在GC上的時間盡可能的少撇眯。

另外,當(dāng)空間不足虱咧,如對象空間分配或轉(zhuǎn)移失敗時熊榛,G1會首先嘗試增加堆空間,如果擴(kuò)容失敗腕巡,則發(fā)起擔(dān)保的Full GC玄坦。

Full GC后,堆尺寸計算結(jié)果也會調(diào)整堆空間绘沉。

分代概念

Generation (分代 )

分代垃圾收集可以將關(guān)注點集中在最近被分配的對象上煎楣,而無需整堆掃描,避免長命對象的拷貝车伞,同時獨立收集有助于降低響應(yīng)時間择懂。

雖然分區(qū)使得內(nèi)存分配不再要求緊湊的內(nèi)存空間,但G1依然使用了分代的思想另玖。

與其他垃圾收集器類似困曙,G1將內(nèi)存在邏輯上劃分為年輕代和老年代,其中年輕代又劃分為Eden空間和Survivor空間谦去。

但年輕代空間并不是固定不變的慷丽,當(dāng)現(xiàn)有年輕代分區(qū)占滿時,JVM會分配新的空閑分區(qū)加入到年輕代空間鳄哭。

整個年輕代內(nèi)存會在初始空間-XX:NewSize與最大空間-XX:MaxNewSize之間動態(tài)變化盈魁,且由參數(shù)目標(biāo)暫停時間-XX:MaxGCPauseMillis、需要擴(kuò)縮容的大小以及分區(qū)的已記憶集合(RSet)計算得到窃诉。

當(dāng)然杨耙,G1依然可以設(shè)置固定的年輕代大小(參數(shù)-XX:NewRatio-Xmn)飘痛,但同時暫停目標(biāo)將失去意義珊膜。

Local allocation buffer (LAB) (本地分配緩沖)

值得注意的是,由于分區(qū)的思想宣脉,每個線程均可以"認(rèn)領(lǐng)"某個分區(qū)Region用于線程本地的內(nèi)存分配漆枚,而不需要顧及分區(qū)是否連續(xù)呻袭。

因此飒泻,每個應(yīng)用線程和GC線程都會獨立的使用分區(qū)阻课,進(jìn)而減少同步時間,提升GC效率习寸,這個分區(qū)Region稱為本地分配緩沖區(qū)(LAB)。

  • 應(yīng)用線程本地緩沖區(qū)TLAB
    應(yīng)用線程可以獨占一個本地緩沖區(qū)(TLAB)來創(chuàng)建的對象,而大部分都會落入Eden區(qū)域(巨型對象或分配失敗除外)感憾,因此TLAB的分區(qū)屬于Eden空間;
  • GC線程本地緩沖區(qū)GCLAB
    每次垃圾收集時令花,每個GC線程同樣可以獨占一個本地緩沖區(qū)(GCLAB)用來轉(zhuǎn)移對象阻桅,每次回收會將對象復(fù)制到Suvivor空間或老年代空間;
  • 晉升本地緩沖區(qū)PLAB
    對于從Eden/Survivor空間晉升(Promotion)到Survivor/老年代空間的對象兼都,同樣有GC獨占的本地緩沖區(qū)進(jìn)行操作嫂沉,該部分稱為晉升本地緩沖區(qū)(PLAB)。

分區(qū)模型

Humongous Object (巨型對象)

一個大小達(dá)到甚至超過分區(qū)Region 50%以上的對象稱為巨型對象(Humongous Object)扮碧。
巨型對象會獨占一個趟章、或多個連續(xù)分區(qū),其中第一個分區(qū)被標(biāo)記為開始巨型(StartsHumongous)慎王,相鄰連續(xù)分區(qū)被標(biāo)記為連續(xù)巨型(ContinuesHumongous)尤揣。
Humongous Object 有以下特點:

  • Humongous Object直接分配到了 老年代,防止了反復(fù)拷貝移動柬祠。

當(dāng)線程為巨型分配空間時北戏,不能簡單在TLAB進(jìn)行分配,因為巨型對象的移動成本很高漫蛔,而且有可能一個分區(qū)不能容納巨型對象嗜愈。
因此,巨型對象會直接在老年代分配莽龟,所占用的連續(xù)空間稱為巨型分區(qū)(Humongous Region)蠕嫁。

  • Humongous ObjectYGC階段, Global Concurrent Marking 階段的 CleanupFGC 階段 回收毯盈。

由于無法享受LAB帶來的優(yōu)化剃毒,并且確定一片連續(xù)的內(nèi)存空間需要掃描整堆Heap,因此確定巨型對象開始位置的成本非常高搂赋,如果可以赘阀,應(yīng)用程序應(yīng)避免生成巨型對象。

  • 在分配Humongous Object 之前先檢查是否超過 initiating heap occupancy percent (由參數(shù)-XX:InitiatingHeapOccupancyPercent控制) 和 the marking threshold脑奠。
    如果超過的話基公,就啟動并發(fā)收集周期Concurrent Marking Cycle ,為的是提早回收宋欺,防止 Evacuation FailureFull GC轰豆。
RSetRemember Set胰伍,已記憶集合)

在串行和并行收集器中,GC通過整堆掃描酸休,來確定對象是否處于可達(dá)路徑中骂租。

然而G1為了避免STW式的整堆Heap掃描,在每個分區(qū)Region記錄了一個已記憶集合(RSet)斑司,內(nèi)部類似一個反向指針渗饮,記錄引用分區(qū)Region內(nèi)對象的卡片Card的索引。

當(dāng)要回收該分區(qū)Region時陡厘,通過掃描分區(qū)的RSet抽米,來確定引用本分區(qū)內(nèi)的對象是否存活特占,進(jìn)而確定本分區(qū)內(nèi)的對象存活情況糙置。

事實上,并非所有的引用都需要記錄在RSet中是目,如果一個分區(qū)Region確定需要掃描谤饭,那么無需RSet也可以無遺漏的得到引用關(guān)系。

那么引用源自本分區(qū)Region的對象懊纳,當(dāng)然不用落入RSet中揉抵;

同時,G1 GC每次都會對年輕代進(jìn)行整體收集嗤疯,因此引用源自年輕代的對象冤今,也不需要在RSet記錄。

最后只有老年代的分區(qū)Region可能會有RSet記錄茂缚,這些分區(qū)稱為擁有RSet分區(qū)(an RSet’s owning region)戏罢。

Per Region Table (PRT)

RSet在內(nèi)部使用Per Region Table(PRT)記錄分區(qū)Region的引用情況。
由于RSet的記錄要占用分區(qū)Region的空間脚囊,如果一個分區(qū)非常"受歡迎"龟糕,那么RSet占用的空間會上升,從而降低分區(qū)Region的可用空間悔耘。
G1應(yīng)對這個問題采用了改變RSet的密度的方式讲岁,在PRT中將會以三種模式記錄引用:

  • 稀少:直接記錄引用對象的卡片Card的索引
  • 細(xì)粒度:記錄引用對象的分區(qū)Region的索引
  • 粗粒度:只記錄引用情況,每個分區(qū)對應(yīng)一個比特位

由上可知衬以,粗粒度的PRT只是記錄了引用數(shù)量缓艳,需要通過整堆Heap掃描才能找出所有引用,因此掃描速度也是最慢的看峻。

CSetCollection Set郎任,收集集合)

收集集合(CSet)代表每次GC暫停時回收的一系列目標(biāo)分區(qū)Region

在任意一次收集暫停中备籽,CSet所有分區(qū)都會被釋放舶治,內(nèi)部存活的對象都會被轉(zhuǎn)移到分配的空閑分區(qū)中分井。

因此無論是年輕代收集,還是混合收集霉猛,工作的機(jī)制都是一致的尺锚。

年輕代收集(YGC)的CSet只容納年輕代分區(qū),而混合收集(Mixed GC)會通過啟發(fā)式算法惜浅,在老年代候選回收分區(qū)中瘫辩,篩選出回收收益最高的分區(qū)添加到CSet中。

  • 候選老年代分區(qū)的CSet準(zhǔn)入條件坛悉,可以通過活躍度閾值-XX:G1MixedGCLiveThresholdPercent(默認(rèn)85%)進(jìn)行設(shè)置伐厌,從而攔截那些回收開銷巨大的對象;

  • 同時裸影,每次混合收集可以包含候選老年代分區(qū)挣轨,可根據(jù)CSet對堆的總大小占比-XX:G1OldCSetRegionThresholdPercent(默認(rèn)10%)設(shè)置數(shù)量上限。

由上述可知轩猩,G1的收集都是根據(jù)CSet進(jìn)行操作的卷扮,年輕代收集(YGC)與混合收集(Mixed GC)沒有明顯的不同,最大的區(qū)別在于兩種收集的觸發(fā)條件均践。

年輕代收集集合 CSet of Young Collection

應(yīng)用線程不斷活動后晤锹,年輕代空間會被逐漸填滿。當(dāng)JVM分配對象到Eden區(qū)域失敗(Eden區(qū)已滿)時彤委,便會觸發(fā)一次STW式的年輕代收集鞭铆。
在年輕代收集中,Eden分區(qū)存活的對象將被拷貝到Survivor分區(qū)焦影;
原有Survivor分區(qū)存活的對象车遂,將根據(jù)任期閾值(tenuring threshold)分別晉升到PLAB中,新的survivor分區(qū)和老年代分區(qū)偷办。而原有的年輕代分區(qū)將被整體回收掉艰额。

同時,年輕代收集還負(fù)責(zé)維護(hù)對象的年齡(存活次數(shù))椒涯,輔助判斷老化(tenuring)對象晉升的時候是到Survivor分區(qū)還是到老年代分區(qū)柄沮。
年輕代收集首先先將晉升對象尺寸總和、對象年齡信息維護(hù)到年齡表中废岂,再根據(jù)年齡表祖搓、Survivor尺寸、Survivor填充容量-XX:TargetSurvivorRatio(默認(rèn)50%)湖苞、最大任期閾值-XX:MaxTenuringThreshold(默認(rèn)15)拯欧,計算出一個恰當(dāng)?shù)娜纹陂撝担彩浅^任期閾值的對象都會被晉升到老年代财骨。

混合收集集合 CSet of Mixed Collection

年輕代收集不斷活動后镐作,老年代的空間也會被逐漸填充藏姐。當(dāng)老年代占用空間超過整堆比IHOP閾值-XX:InitiatingHeapOccupancyPercent(默認(rèn)45%)時,G1就會啟動一次混合垃圾收集周期该贾。

為了滿足暫停目標(biāo)羔杨,G1可能不能一口氣將所有的候選分區(qū)收集掉,因此G1可能會產(chǎn)生連續(xù)多次的混合收集與應(yīng)用線程交替執(zhí)行杨蛋,每次STW的混合收集與年輕代收集過程相類似兜材。

  • 為了確定包含到年輕代收集集合CSet的老年代分區(qū),JVM通過參數(shù)混合周期的最大總次數(shù)-XX:G1MixedGCCountTarget(默認(rèn)8)逞力、堆廢物百分比-XX:G1HeapWastePercent(默認(rèn)5%)曙寡。

通過候選老年代分區(qū)總數(shù)與混合周期最大總次數(shù),確定每次包含到CSet的最小分區(qū)數(shù)量寇荧;

根據(jù)堆廢物百分比举庶,當(dāng)收集達(dá)到參數(shù)時,不再啟動新的混合收集砚亭。而每次添加到CSet的分區(qū)灯变,則通過計算得到的GC效率進(jìn)行安排殴玛。

G1的活動周期

G1的垃圾回收包括了以下幾種:

  • Concurrent Marking Cycle (并發(fā)收集)
    類似 CMS的并發(fā)收集過程捅膘。

  • Young Collection (YGC,年輕代收集滚粟,STW

  • Mixed Collection Cycle (混合收集寻仗,STW

  • Full GC(FGC, STW
    JDK10以前FGC是串行回收凡壤,JDK10+可以是并行回收署尤。

并發(fā)標(biāo)記周期 Concurrent Marking Cycle

并發(fā)標(biāo)記周期是G1中非常重要的階段,這個階段將會為混合收集周期識別垃圾最多的老年代分區(qū)亚侠。

整個周期完成根標(biāo)記曹体、識別所有(可能)存活對象,并計算每個分區(qū)的活躍度硝烂,從而確定GC效率等級箕别。

當(dāng)達(dá)到IHOP閾值-XX:InitiatingHeapOccupancyPercent(老年代占整堆比,默認(rèn)45%)時滞谢,便會觸發(fā)并發(fā)標(biāo)記周期串稀。

整個并發(fā)標(biāo)記周期將由初始標(biāo)記(Initial Mark)、根分區(qū)掃描(Root Region Scanning)狮杨、并發(fā)標(biāo)記(Concurrent Marking)母截、重新標(biāo)記(Remark)、清除(Cleanup)幾個階段組成橄教。

其中清寇,初始標(biāo)記(隨年輕代收集一起活動)喘漏、重新標(biāo)記、清除是STW的华烟,而并發(fā)標(biāo)記如果來不及標(biāo)記存活對象陷遮,則可能在并發(fā)標(biāo)記過程中,G1又觸發(fā)了幾次年輕代收集(YGC)垦江。

Initial Marking (初始標(biāo)記帽馋, STW)

它標(biāo)記了從GC Root開始直接可達(dá)的對象。

事實上比吭,當(dāng)達(dá)到IHOP閾值時绽族,G1并不會立即發(fā)起并發(fā)標(biāo)記周期,而是等待下一次年輕代收集衩藤,利用年輕代收集的STW時間段吧慢,完成初始標(biāo)記,這種方式稱為借道(Piggybacking)赏表。

Root region scanning (根分區(qū)掃描)

在初始標(biāo)記暫停結(jié)束后检诗,年輕代收集也完成的對象復(fù)制到Survivor的工作,應(yīng)用線程開始活躍起來瓢剿。此時為了保證標(biāo)記算法的正確性逢慌,所有新復(fù)制到Survivor分區(qū)的對象,都需要被掃描并標(biāo)記成根间狂,這個過程稱為根分區(qū)掃描(Root Region Scanning)攻泼,同時掃描的Suvivor分區(qū)也被稱為根分區(qū)(Root Region)。

Concurrent Marking(并發(fā)標(biāo)記)

這個階段從GC Root開始對heap中的對象標(biāo)記鉴象,標(biāo)記線程與應(yīng)用程序線程并行執(zhí)行忙菠,并且收集各個Region的存活對象信息。
和應(yīng)用線程并發(fā)執(zhí)行纺弊,并發(fā)標(biāo)記線程在并發(fā)標(biāo)記階段啟動牛欢,由參數(shù)-XX:ConcGCThreads(默認(rèn)GC線程數(shù)的1/4,即-XX:ParallelGCThreads/4)控制啟動數(shù)量淆游,
每個線程每次只掃描一個分區(qū)Region傍睹,從而標(biāo)記出存活對象圖。

所有的標(biāo)記任務(wù)必須在堆滿前就完成掃描稽犁,如果并發(fā)標(biāo)記耗時很長焰望,那么有可能在并發(fā)標(biāo)記過程中,又經(jīng)歷了幾次年輕代收集已亥。
如果堆滿前沒有完成標(biāo)記任務(wù)熊赖,則會觸發(fā)擔(dān)保機(jī)制,經(jīng)歷一次長時間的串行Full GC虑椎。

Remark ( 重新標(biāo)記震鹉,STW)

標(biāo)記那些在并發(fā)標(biāo)記階段發(fā)生變化的對象俱笛,將被回收。
這個階段也是并行執(zhí)行的传趾,通過參數(shù)-XX:ParallelGCThread可設(shè)置GC暫停時可用的GC線程數(shù)迎膜。

Cleanup (清理,STW)

清除階段主要執(zhí)行以下操作:

  • RSet梳理浆兰,啟發(fā)式算法會根據(jù)活躍度和RSet尺寸對分區(qū)定義不同等級磕仅,同時RSet數(shù)理也有助于發(fā)現(xiàn)無用的引用。參數(shù)-XX:+PrintAdaptiveSizePolicy可以開啟打印啟發(fā)式算法決策細(xì)節(jié)簸呈;
  • 整理堆分區(qū)榕订,為混合收集周期識別回收收益高(基于釋放空間和暫停目標(biāo))的老年代分區(qū)集合;
  • 識別所有空閑分區(qū)蜕便,即發(fā)現(xiàn)無存活對象的分區(qū)劫恒。該分區(qū)可在清除階段直接回收,無需等待下次收集周期轿腺。

年輕代收集 Young Collection /混合收集周期 Mixed Collection Cycle

當(dāng)應(yīng)用運行開始時两嘴,堆內(nèi)存可用空間還比較大,只會在年輕代滿時族壳,觸發(fā)年輕代收集憔辫;

隨著老年代內(nèi)存增長,當(dāng)?shù)竭_(dá)IHOP閾值-XX:InitiatingHeapOccupancyPercent(老年代占整堆比决侈,默認(rèn)45%)時螺垢,G1開始著手準(zhǔn)備收集老年代空間喧务。

首先經(jīng)歷并發(fā)標(biāo)記周期 Concurrent Marking Cycle赖歌,識別出高收益的老年代分區(qū),前文已述功茴。

但隨后G1并不會馬上開始一次混合收集庐冯,而是讓應(yīng)用線程先運行一段時間,等待觸發(fā)一次年輕代收集坎穿。

在這次STW中展父,G1將保準(zhǔn)整理混合收集周期。接著再次讓應(yīng)用線程運行玲昧,當(dāng)接下來的幾次年輕代收集時栖茉,將會有老年代分區(qū)加入到CSet中,

即觸發(fā)混合收集孵延,這些連續(xù)多次的混合收集稱為混合收集周期(Mixed Collection Cycle)吕漂。

年輕代收集 Young Collection,YGC

每次收集過程中,既有并行執(zhí)行的活動尘应,也有串行執(zhí)行的活動惶凝,但都可以是多線程的吼虎。

在并行執(zhí)行的任務(wù)中,如果某個任務(wù)過重苍鲜,會導(dǎo)致其他線程在等待某項任務(wù)的處理思灰,需要對這些地方進(jìn)行優(yōu)化。

以下部分部分可以結(jié)合日志查看

  • 并行活動

    • 外部根分區(qū)掃描 Ext Root Scanning:
      此活動對堆外的根(JVM系統(tǒng)目錄混滔、VM數(shù)據(jù)結(jié)構(gòu)洒疚、JNI線程句柄、硬件寄存器坯屿、全局變量拳亿、線程對棧根)進(jìn)行掃描,發(fā)現(xiàn)那些沒有加入到暫停收集集合CSet中的對象愿伴。如果系統(tǒng)目錄(單根)擁有大量加載的類肺魁,最終可能其他并行活動結(jié)束后,該活動依然沒有結(jié)束而帶來的等待時間隔节。

    • 更新已記憶集合 Update RS:
      并發(fā)優(yōu)化線程會對臟卡片的分區(qū)進(jìn)行掃描更新日志緩沖區(qū)來更新RSet鹅经,但只會處理全局緩沖列表。作為補充怎诫,所有被記錄但是還沒有被優(yōu)化線程處理的剩余緩沖區(qū)瘾晃,會在該階段處理,變成已處理緩沖區(qū)(Processed Buffers)幻妓。為了限制花在更新RSet的時間蹦误,可以設(shè)置暫停占用百分比-XX:G1RSetUpdatingPauseTimePercent(默認(rèn)10%,即-XX:MaxGCPauseMills/10)肉津。值得注意的是强胰,如果更新日志緩沖區(qū)更新的任務(wù)不降低,單純地減少RSet的更新時間妹沙,會導(dǎo)致暫停中被處理的緩沖區(qū)減少偶洋,將日志緩沖區(qū)更新工作推到并發(fā)優(yōu)化線程上,從而增加對Java應(yīng)用線程資源的爭奪距糖。

    • RSet掃描 Scan RS:
      在收集當(dāng)前CSet之前玄窝,考慮到分區(qū)外的引用,必須掃描CSet分區(qū)的RSet悍引。如果RSet發(fā)生粗化恩脂,則會增加RSet的掃描時間。
      開啟診斷模式-XX:UnlockDiagnosticVMOptions后趣斤,
      通過參數(shù)-XX:+G1SummarizeRSetStats可以確定并發(fā)優(yōu)化線程是否能夠及時處理更新日志緩沖區(qū)俩块,并提供更多的信息,來幫助為RSet粗化總數(shù)提供窗口。
      參數(shù)-XX:G1SummarizeRSetStatsPeriod=n可設(shè)置RSet的統(tǒng)計周期典阵,即經(jīng)歷多少此GC后進(jìn)行一次統(tǒng)計

    • 代碼根掃描 Code Root Scanning:對代碼根集合進(jìn)行掃描奋渔,掃描JVM編譯后代碼Native Method的引用信息(nmethod掃描),進(jìn)行RSet掃描壮啊。事實上嫉鲸,只有CSet分區(qū)中的RSet有強(qiáng)代碼根時,才會做nmethod掃描歹啼,查找對CSet的引用玄渗。

    • 轉(zhuǎn)移和回收 Object Copy:
      通過選定的CSet以及CSet分區(qū)完整的引用集,將執(zhí)行暫停時間的主要部分:CSet分區(qū)存活對象的轉(zhuǎn)移狸眼、CSet分區(qū)空間的回收藤树。通過工作竊取機(jī)制來負(fù)載均衡地選定復(fù)制對象的線程,并且復(fù)制和掃描對象被轉(zhuǎn)移的存活對象將拷貝到每個GC線程分配緩沖區(qū)GCLAB拓萌。G1會通過計算岁钓,預(yù)測分區(qū)復(fù)制所花費的時間,從而調(diào)整年輕代的尺寸微王。

    • 終止 Termination:
      完成上述任務(wù)后屡限,如果任務(wù)隊列已空,則工作線程會發(fā)起終止要求炕倘。如果還有其他線程繼續(xù)工作钧大,空閑的線程會通過工作竊取機(jī)制嘗試幫助其他線程處理。而單獨執(zhí)行根分區(qū)掃描的線程罩旋,如果任務(wù)過重啊央,最終會晚于終止。

    • GC外部的并行活動 GC Worker Other:
      該部分并非GC的活動涨醋,而是JVM的活動導(dǎo)致占用了GC暫停時間(例如JNI編譯)瓜饥。

  • 串行活動

    • 代碼根更新 Code Root Fixup:根據(jù)轉(zhuǎn)移對象更新代碼根。

    • 代碼根清理 Code Root Purge:清理代碼根集合表东帅。

    • 清除全局卡片標(biāo)記 Clear CT:在任意收集周期會掃描CSet與RSet記錄的PRT压固,掃描時會在全局卡片表中進(jìn)行標(biāo)記,防止重復(fù)掃描靠闭。在收集周期的最后將會清除全局卡片表中的已掃描標(biāo)志。

    • 選擇下次收集集合 Choose CSet:該部分主要用于并發(fā)標(biāo)記周期后的年輕代收集坎炼、以及混合收集中愧膀,在這些收集過程中,由于有老年代候選分區(qū)的加入谣光,往往需要對下次收集的范圍做出界定檩淋;但單純的年輕代收集中,所有收集的分區(qū)都會被收集,不存在選擇蟀悦。

    • 引用處理 Ref Proc:主要針對軟引用媚朦、弱引用、虛引用日戈、final引用询张、JNI引用。當(dāng)Ref Proc占用時間過多時浙炼,可選擇使用參數(shù)-XX:ParallelRefProcEnabled激活多線程引用處理份氧。G1希望應(yīng)用能小心使用軟引用,因為軟引用會一直占據(jù)內(nèi)存空間直到空間耗盡時被Full GC回收掉弯屈;即使未發(fā)生Full GC蜗帜,軟引用對內(nèi)存的占用,也會導(dǎo)致GC次數(shù)的增加资厉。

    • 引用排隊 Ref Enq:此項活動可能會導(dǎo)致RSet的更新厅缺,此時會通過記錄日志,將關(guān)聯(lián)的卡片標(biāo)記為臟卡片宴偿。

    • 卡片重新臟化 Redirty Cards:重新臟化卡片店归。

    • 回收空閑巨型分區(qū) Humongous Reclaim:G1做了一個優(yōu)化:通過查看所有根對象以及年輕代分區(qū)的RSet,如果確定RSet中巨型對象沒有任何引用酪我,則說明G1發(fā)現(xiàn)了一個不可達(dá)的巨型對象消痛,該對象分區(qū)會被回收。

    • 釋放分區(qū) Free CSet:回收CSet分區(qū)的所有空間都哭,并加入到空閑分區(qū)中秩伞。

    • 其他活動 Other:GC中可能還會經(jīng)歷其他耗時很小的活動,如修復(fù)JNI句柄等欺矫。

并發(fā)標(biāo)記周期后的年輕代收集 Young Collection Following Concurrent Marking Cycle

當(dāng)G1發(fā)起并發(fā)標(biāo)記周期之后纱新,并不會馬上開始混合收集。
G1會先等待下一次年輕代收集穆趴,然后在該收集階段中脸爱,確定下次混合收集的CSet(Choose CSet)。

混合收集周期 Mixed Collection Cycle, Mixed GC

單次的混合收集與年輕代收集并無二致未妹。

根據(jù)暫停目標(biāo)簿废,老年代的分區(qū)可能不能一次暫停收集中被處理完,G1會發(fā)起連續(xù)多次的混合收集络它,稱為混合收集周期(Mixed Collection Cycle)族檬。

G1會計算每次加入到CSet中的分區(qū)數(shù)量、混合收集進(jìn)行次數(shù)化戳,并且在上次的年輕代收集单料、以及接下來的混合收集中,G1會確定下次加入CSet的分區(qū)集(Choose CSet),并且確定是否結(jié)束混合收集周期扫尖。

轉(zhuǎn)移失敗的擔(dān)保機(jī)制 Full GC

轉(zhuǎn)移失敗(Evacuation Failure)是指當(dāng)G1無法在堆空間中申請新的分區(qū)時白对,G1便會觸發(fā)擔(dān)保機(jī)制,執(zhí)行一次STW式的换怖、單線程(JDK10支持多線程)的Full GC甩恼。

Full GC會對整堆做標(biāo)記清除和壓縮,最后將只包含純粹的存活對象狰域。參數(shù)-XX:G1ReservePercent(默認(rèn)10%)可以保留空間媳拴,來應(yīng)對晉升模式下的異常情況,最大占用整堆50%兆览,更大也無意義屈溉。

G1在以下場景中會觸發(fā)Full GC,同時會在日志中記錄to-space exhausted以及Evacuation Failure

  • 從年輕代分區(qū)拷貝存活對象時抬探,無法找到可用的空閑分區(qū)
  • 從老年代分區(qū)轉(zhuǎn)移存活對象時子巾,無法找到可用的空閑分區(qū)
  • 分配巨型對象Humongous Object 時在老年代無法找到足夠的連續(xù)分區(qū)

由于G1的應(yīng)用場合往往堆內(nèi)存都比較大,所以Full GC的收集代價非常昂貴小压,應(yīng)該避免Full GC的發(fā)生线梗。

問題

  • 什么時候觸發(fā)concurrent marking ?
# 啟動并發(fā)周期 Concurrent Marking Cycle (以及后續(xù)的混合周期 MixedGC)時的堆內(nèi)存占用百分比. G1用它來觸發(fā)并發(fā)GC周期,基于整個堆的使用率,而不只是某一代內(nèi)存的使用比例怠益。默認(rèn)45%
# 當(dāng)堆存活對象占用堆的45%仪搔,就會啟動G1 中并發(fā)標(biāo)記周期 Concurrent Marking Cycle
-XX:InitiatingHeapOccupancyPercent
  • 什么時候發(fā)生Mixed GC?
    concurrent marking 主要是為Mixed GC提供標(biāo)記服務(wù)的,并不是一次GC過程的一個必須環(huán)節(jié)蜻牢。

    由一些參數(shù)控制烤咧,另外也控制著哪些老年代Region會被選入CSet(收集集合)。

# 一次 concurrent marking之后抢呆,最多執(zhí)行Mixed GC的次數(shù)(默認(rèn)8)
-XX:G1MixedGCCountTarget
# 堆廢物百分比(默認(rèn)5%)煮嫌,在每次YGC之后和再次發(fā)生Mixed GC之前,會檢查垃圾占比是否達(dá)到此參數(shù)抱虐,只有達(dá)到了昌阿,下次才會發(fā)生Mixed GC。
-XX:G1HeapWastePercent
# old generation region中的存活對象的占比恳邀,只有在此參數(shù)之下懦冰,才會被選入CSet。
-XX:G1MixedGCLiveThresholdPercent
# 一次Mixed GC中能被選入CSet的最多old generation region數(shù)量轩娶。
-XX:G1OldCSetRegionThresholdPercent

GC日志詳解

并發(fā)標(biāo)記周期 Concurrent Marking Cycle
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0094252 secs]
# 根分區(qū)掃描儿奶,可能會被 YGC 打斷,那么結(jié)束就是如:[GC pause (G1 Evacuation Pause) (young)[GC concurrent-root-region-scan-end, 0.0007157 secs]
[GC concurrent-mark-start]
[GC concurrent-mark-end, 0.0203881 secs]
# 并發(fā)標(biāo)記階段
[GC remark [Finalize Marking, 0.0007822 secs] [GC ref-proc, 0.0005279 secs] [Unloading, 0.0013783 secs], 0.0036513 secs]
#  重新標(biāo)記鳄抒,STW
 [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC cleanup 13985K->13985K(20480K), 0.0034675 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
# 清除
年輕代收集 YGC
[GC pause (G1 Evacuation Pause) (young), 0.0022483 secs]
# young -> 年輕代      Evacuation-> 復(fù)制存活對象 
   [Parallel Time: 1.0 ms, GC Workers: 10] # 并發(fā)執(zhí)行的GC線程數(shù),以下階段是并發(fā)執(zhí)行的
      [GC Worker Start (ms): Min: 109.0, Avg: 109.1, Max: 109.1, Diff: 0.2] 
      [Ext Root Scanning (ms): Min: 0.1, Avg: 0.2, Max: 0.3, Diff: 0.2, Sum: 2.3] # 外部根分區(qū)掃描
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] # 更新已記憶集合 Update RSet,檢測從年輕代指向老年代的對象
         [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0] 
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]# RSet掃描
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] # 代碼根掃描
      [Object Copy (ms): Min: 0.3, Avg: 0.3, Max: 0.4, Diff: 0.1, Sum: 3.5] # 轉(zhuǎn)移和回收许溅,拷貝存活的對象到survivor/old區(qū)域
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] # 完成上述任務(wù)后瓤鼻,如果任務(wù)隊列已空,則工作線程會發(fā)起終止要求贤重。
         [Termination Attempts: Min: 1, Avg: 5.8, Max: 9, Diff: 8, Sum: 58]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1] # GC外部的并行活動茬祷,該部分并非GC的活動,而是JVM的活動導(dǎo)致占用了GC暫停時間(例如JNI編譯)并蝗。
      [GC Worker Total (ms): Min: 0.5, Avg: 0.6, Max: 0.7, Diff: 0.2, Sum: 5.9]
      [GC Worker End (ms): Min: 109.7, Avg: 109.7, Max: 109.7, Diff: 0.0]
   [Code Root Fixup: 0.0 ms] # 串行任務(wù)祭犯,根據(jù)轉(zhuǎn)移對象更新代碼根
   [Code Root Purge: 0.0 ms] #串行任務(wù), 代碼根清理
   [Clear CT: 0.5 ms] #串行任務(wù)滚停,清除全局卡片 Card Table 標(biāo)記
   [Other: 0.8 ms]
      [Choose CSet: 0.0 ms] # 選擇下次收集集合  CSet
      [Ref Proc: 0.4 ms] # 引用處理 Ref Proc沃粗,處理軟引用、弱引用键畴、虛引用最盅、final引用、JNI引用
      [Ref Enq: 0.0 ms] # 引用排隊 Ref Enq
      [Redirty Cards: 0.3 ms] # 卡片重新臟化 Redirty Cards:重新臟化卡片
      [Humongous Register: 0.0 ms] 
      [Humongous Reclaim: 0.0 ms] # 回收空閑巨型分區(qū) Humongous Reclaim起惕,通過查看所有根對象以及年輕代分區(qū)的RSet涡贱,如果確定RSet中巨型對象沒有任何引用,該對象分區(qū)會被回收惹想。
      [Free CSet: 0.0 ms]  # 釋放分區(qū) Free CSet
   [Eden: 12288.0K(12288.0K)->0.0B(11264.0K) Survivors: 0.0B->1024.0K Heap: 12288.0K(20480.0K)->832.0K(20480.0K)]
 [Times: user=0.01 sys=0.00, real=0.00 secs] 
# 從年輕代分區(qū)拷貝存活對象時问词,無法找到可用的空閑分區(qū)
# 從老年代分區(qū)轉(zhuǎn)移存活對象時,無法找到可用的空閑分區(qū) 這兩種情況之一導(dǎo)致的 YGC
[GC pause (G1 Evacuation Pause) (young) (to-space exhausted), 0.0916534 secs]
# 并發(fā)標(biāo)記周期 Concurrent Marking Cycle 中的 根分區(qū)掃描階段嘀粱,被 YGC中斷
[GC pause (G1 Evacuation Pause) (young)[GC concurrent-root-region-scan-end, 0.0007157 secs]
混合收集周期 Mixed Collection Cycle, Mixed GC
# 并發(fā)標(biāo)記周期 Concurrent Marking Cycle 的開始
[GC pause (G1 Evacuation Pause) (young) (initial-mark) , 0.0443460 secs]
Full GC
[Full GC (Allocation Failure) 20480K->9656K(20480K), 0.0189481 secs]
   [Eden: 0.0B(1024.0K)->0.0B(5120.0K) Survivors: 0.0B->0.0B Heap: 20480.0K(20480.0K)->9656.8K(20480.0K)], [Metaspace: 4960K->4954K(1056768K)]
 [Times: user=0.03 sys=0.00, real=0.02 secs] 

參考資料

by Sven Augustus https://my.oschina.net/langxSpirit

本文由博客群發(fā)一文多發(fā)等運營工具平臺 OpenWrite 發(fā)布

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末激挪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子草穆,更是在濱河造成了極大的恐慌灌灾,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悲柱,死亡現(xiàn)場離奇詭異锋喜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)豌鸡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門嘿般,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涯冠,你說我怎么就攤上這事炉奴。” “怎么了蛇更?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵瞻赶,是天一觀的道長赛糟。 經(jīng)常有香客問我,道長砸逊,這世上最難降的妖魔是什么璧南? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮师逸,結(jié)果婚禮上司倚,老公的妹妹穿的比我還像新娘。我一直安慰自己篓像,他們只是感情好动知,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著员辩,像睡著了一般盒粮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屈暗,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天拆讯,我揣著相機(jī)與錄音,去河邊找鬼养叛。 笑死种呐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弃甥。 我是一名探鬼主播爽室,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淆攻!你這毒婦竟也來了阔墩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤瓶珊,失蹤者是張志新(化名)和其女友劉穎啸箫,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伞芹,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡忘苛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唱较。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扎唾。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖南缓,靈堂內(nèi)的尸體忽然破棺而出千扶,到底是詐尸還是另有隱情本昏,我是刑警寧澤祠肥,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站倍阐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏薄腻。R本人自食惡果不足惜收捣,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一届案、第九天 我趴在偏房一處隱蔽的房頂上張望庵楷。 院中可真熱鬧,春花似錦楣颠、人聲如沸尽纽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弄贿。三九已至,卻和暖如春矫膨,著一層夾襖步出監(jiān)牢的瞬間差凹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工侧馅, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留危尿,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓馁痴,卻偏偏與公主長得像谊娇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子罗晕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348