Java 垃圾收集器

1、CMS收集器

CMS收集器是老年代垃圾收集器昧廷,可以與Serial收集器、Parallel New收集器搭配使用眉枕。

image.png

標(biāo)記收集的線程可以和用戶線程同時工作速挑。

CMS收集器采用的標(biāo)記-清除算法, 在CMS收集器中腊满, 標(biāo)記清除算法分為4個階段:
1碳蛋、初始標(biāo)記: 需要停頓肃弟,標(biāo)記一下GC Roots可以直接關(guān)聯(lián)到的對象穷缤,速度很快。
2快耿、并發(fā)標(biāo)記:不需要停頓掀亥, 進行GC Roots Tracing的過程搪花。
3、重新標(biāo)記: 需要停頓幢踏,修正并發(fā)標(biāo)記階段用戶程序繼續(xù)用作而產(chǎn)生新的變動的那一部分對象的標(biāo)記記錄房蝉。
4搭幻、并發(fā)清除: 不需要停頓。
其中并發(fā)標(biāo)記和并發(fā)清除2個重要階段不需要停頓俯逾,如下圖所示:

image.png

優(yōu)點: 并發(fā)收集、低停頓

缺點

  • CMS收集器對CPU比較敏感, 低停頓時間是以犧牲吞吐量為代價善已,CPU利用率不高。
  • CMS收集器無法處理浮動垃圾, 浮動垃圾是由于用戶程序繼續(xù)運行而產(chǎn)生的垃圾悉稠。如果CMS收集器運行期間預(yù)留的內(nèi)存不足以進行空間擔(dān)保,虛擬機將啟動預(yù)備方案卦尊,啟動Serial Old收集器進行收集岂却, 耗時會比較長。
  • CMS收集器采用標(biāo)記-清除算法扫尺, 會產(chǎn)生空間碎片器联〔ν兀空間碎片過多時渣磷,會導(dǎo)致空間雖然很多, 但是沒有足夠的連續(xù)空間來分配大對象形纺,而需要提前進行Full GC逐样。CMS收集器會在頂不住的時候挪捕,要進行FullGC時啟動空間整理, 內(nèi)存整理的過程無法并發(fā)奏纪。

參數(shù)控制
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection Full GC后序调,進行一次碎片整理;整理過程是獨占的男韧,會引起停頓時間變長
-XX:+CMSFullGCsBeforeCompaction 設(shè)置進行幾次Full GC后甚纲,進行一次碎片整理
-XX:ParallelCMSThreads 設(shè)定CMS的線程數(shù)量(一般情況約等于可用CPU數(shù)量)

空間分配擔(dān)保

在發(fā)生Minor GC之前,虛擬機會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間韭寸,如果這個條件成立赴背,那么Minor GC可以確保是安全的凰荚。當(dāng)大量對象在Minor GC后仍然存活,就需要老年代進行空間分配擔(dān)保到涂,把Survivor無法容納的對象直接進入老年代养盗。如果老年代判斷到剩余空間不足(根據(jù)以往每一次回收晉升到老年代對象空間的平均值作為經(jīng)驗值)往核,則進行一次Full GC。

CMS收集器完整步驟
1) Initial Mark(初始標(biāo)記)
初始標(biāo)記階段是CMS流程中需要中斷用戶線程的2個階段其中之一衩婚,這個階段的目標(biāo):標(biāo)記那些被GC Roots直接引用或者被年輕代存活對象引用的所有對象非春。如圖所示:

上面有些對象是被GC Roots引用, 有些是被年輕代對象引用储耐。

2)Concurrent Mark(并發(fā)標(biāo)記)
在這個階段垃圾收集器會并發(fā)遍歷所有老年代對象什湘, 然后標(biāo)記所有存活的對象, 它會根據(jù)上個階段找到GC ROOTS遍歷查找脯颜。 并發(fā)標(biāo)記階段热幔,不會中斷用戶線程绎巨。 并不是所有老年代的對象都會被標(biāo)記戈锻, 因為用戶線程在進行過程中引用會變化格遭。

image.png

如上圖所示, 與階段1圖對比璧微, 有個對象引用已經(jīng)發(fā)生變化, 如標(biāo)黑的那個對象屹电。

3) Concurrent PreClean(并發(fā)預(yù)先清除)
這個階段不會中斷用戶線程危号,與用戶線程一起工作。在并發(fā)標(biāo)記的過程中苍狰, 一些對象引用已經(jīng)發(fā)生變化淋昭,但是這種情況發(fā)生時翔忽,JVM會將包含這個對象的區(qū)域標(biāo)記為Dirty Card, 這就是Card Marking。
在pre-clean階段材失, 那些能從dirty card到達的對象也會被標(biāo)記龙巨,這個標(biāo)記做完后诗赌, dirty card標(biāo)記就會被清除了铭若。

image.png

上圖中偶宫,紅色區(qū)域表示Dirty , 能被它直接到達的對象也會被標(biāo)記纯趋,標(biāo)記完則Dirty Card被清除吵冒。如下:
image.png

4)Concurrent Abortable PreClean(并發(fā)可能失敗的預(yù)先清除)
這也是一個并發(fā)階段亿汞,但是同樣不會影響用戶的應(yīng)用線程疗我,這個階段是為了盡量承擔(dān)STW(stop-the-world)中最終標(biāo)記階段的工作吴裤。這個階段持續(xù)時間依賴于很多的因素,由于這個階段是在重復(fù)做很多相同的工作剖膳,直接滿足一些條件(比如:重復(fù)迭代的次數(shù)吱晒、完成的工作量或者時鐘時間等)
5) Final Remark(最終重新標(biāo)記)
這是第二個STW階段柜候,也是CMS中的最后一個渣刷,這個階段的目標(biāo)是標(biāo)記老年代所有的存活對象,由于之前的階段是并發(fā)執(zhí)行的碌嘀,GC線程可能跟不上應(yīng)用程序的變化股冗,為了完成標(biāo)記老年代所有存活對象的目標(biāo)止状,STW就非常有必要了。
通常CMS的Final Remark階段會在年代代盡可能干凈的時候運行集峦,目的是為了減少連續(xù)STW發(fā)生的可能性(年輕代存活對象過多的話塔淤,也會導(dǎo)致老年代涉及的存活對象會很多)。這個階段會比前面的幾個階段更復(fù)雜一些妨马。

經(jīng)歷以上5個階段烘跺, 老年代所有存活的對象已經(jīng)被標(biāo)記過了, 現(xiàn)在可以通過清除算法去清除那些老年代不再使用的對象了脖咐。

6)Concurrent Sweep(并發(fā)清除)
這里不需要中斷用戶線程屁擅, 它會與用戶線程一起工作派歌。主要目標(biāo)是:清理那些老年代不再存活的對象匾嘱, 回收它們占用的空間將來使用霎烙。如圖:

image.png

7)Concurrent Reset(并發(fā)重置)
這個階段也是并發(fā)執(zhí)行的,它會重設(shè)CMS內(nèi)部的數(shù)據(jù)結(jié)構(gòu),為下次的GC做準(zhǔn)備。

2昧捷、G1收集器

2.1靡挥、 概述

G1回收器是Java7 update4 之后引入的一個新的垃圾回收器。 G1是一個分代的毒返、增量的、并行與并發(fā)的標(biāo)記盆赤、使用復(fù)制算法的垃圾回收器牺六。 它的設(shè)計目標(biāo)是為了適應(yīng)不斷增大的處理器和內(nèi)存汇鞭,進一步降低暫停時間,同時兼顧良好的吞吐量读整。

G1回收器和CMS回收器的不同:

  • G1回收器采用復(fù)制算法米间, 因此內(nèi)存空間是連續(xù)的,不會出現(xiàn)空間碎片逻锐。 避免了CMS算法因為不連續(xù)空間帶來的問題, 不連續(xù)空間意味著不必采用空閑鏈表的內(nèi)存分配方式盏档,而是可以采用bump-the-pointer的方式。
  • G1回收器和CMS回收器要求的內(nèi)存模型存在極大不同勺拣。 G1回收器將內(nèi)存劃分為一個個固定大小的region, 每個region可以是年輕代、老年代愤惰, 內(nèi)存回收是以region為單元
  • G1回收器還有一個及其重要的特性: 軟實時扇单。 實時垃圾回收的意思是:在要求的時間內(nèi)完成垃圾回收。軟實時則是奠旺,用戶可以指定垃圾回收的時間蜘澜, G1會努力在這個時限內(nèi)完成垃圾回收。

G1收集器的應(yīng)用場景
G1收集器主要應(yīng)用于多CPU大內(nèi)存的場景下响疚,在滿足高吞吐量的場景下鄙信, 盡可能滿足垃圾回收時的暫停時間:

  • 服務(wù)端多核鸦采, 內(nèi)存空間較大的應(yīng)用(至少大于4G)咱旱。
  • 應(yīng)用會產(chǎn)生大量內(nèi)存碎片、需要經(jīng)常壓縮內(nèi)存空間崎苗。
  • 想要更可控、可預(yù)期的GC停頓時間,防止高并發(fā)下雪崩。

2.2、G1的內(nèi)存模型

2.2.1、分區(qū)概念

G1分區(qū)示意圖:


image.png
image.png

1)分區(qū)(Region)
G1采用了分區(qū)(Region)的思路踩叭, 將整個堆劃分為若干個大小相等的區(qū)域, 每次分配對象空間將逐段的使用內(nèi)存满力。因此在堆得使用上,G1不要求對象的存儲一定是連續(xù)的空間, 只要邏輯上連續(xù)即可。每個分區(qū)也不會確定地為某個代服務(wù),可以按需在年輕代和老年代之間切換。啟動時可以指定參數(shù)-XX:G1HeapRegionSize=n可指定分區(qū)大小,默認(rèn)將整堆劃分為2048個分區(qū)。

2) 卡片(Card)
每個分區(qū)內(nèi)部又劃分為若干個卡片(Card)熬词, 表示堆內(nèi)存最小可用粒度所有分區(qū)的卡片將會記錄在全局卡片表(Global Card Table)中寄猩,分配的對象會占用物理上連續(xù)的多個卡片(Card)泊柬。每次對內(nèi)存的回收剪况, 都時對指定分區(qū)的卡片進行處理巡语。

3)堆(Heap)
G1同樣可以通過-Xms/-Xmx來指定堆空間大小。當(dāng)發(fā)生年輕代收集或混合收集時,通過計算GC與應(yīng)用的耗費時間比,自動調(diào)整堆空間大小。如果GC頻率太高,則通過增加堆尺寸弓熏,來減少GC頻率均抽,相應(yīng)地GC占用的時間也隨之降低贤牛;目標(biāo)參數(shù)-XX:GCTimeRatio即為GC與應(yīng)用的耗費時間比椭微,G1默認(rèn)為9侧漓,而CMS默認(rèn)為99泽谨,因為CMS的設(shè)計原則是耗費在GC上的時間盡可能的少。另外鳄炉,當(dāng)空間不足空凸,如對象空間分配或轉(zhuǎn)移失敗時滓窍,G1會首先嘗試增加堆空間杠园,如果擴容失敗爷辙,則發(fā)起擔(dān)保的Full GC臊旭。Full GC后,堆尺寸計算結(jié)果也會調(diào)整堆空間。

2.2.2孔轴、 分代模型

image.png

1) 分代垃圾收集
分代垃圾收集可以將關(guān)注點集中在最近被分配的對象上碑诉, 而無需整堆收集, 避免長命對象的拷貝右蒲, 而獨立收集可以有助于降低響應(yīng)時間。G1依然使用了分代思想, 與其他垃圾回收器類似尽棕, G1將每個區(qū)邏輯上劃分為年輕代和老年代歉提, 其中年輕代又劃分為Eden空間和Survivor空間。但年輕代空間并不是固定不變的,當(dāng)現(xiàn)有年輕代分區(qū)占滿時,JVM會分配新的空閑分區(qū)加入到年輕代空間。

整個年輕代內(nèi)存會在初始空間-XX:G1NewSizePercent(默認(rèn)整堆5%)與最大空間(默認(rèn)60%)之間動態(tài)變化蛋逾,且由參數(shù)目標(biāo)暫停時間-XX:MaxGCPauseMillis(默認(rèn)200ms)、需要擴縮容的大小以-XX:G1MaxNewSizePercent及分區(qū)的已記憶集合(RSet)計算得到茵瀑。當(dāng)然妄荔,G1依然可以設(shè)置固定的年輕代大小(參數(shù)-XX:NewRatio举农、-Xmn)勺像,但同時暫停目標(biāo)將失去意義娃磺。

2) 本地分配緩沖

值得注意的是,由于分區(qū)的思想晌梨,每個線程均可以"認(rèn)領(lǐng)"某個分區(qū)用于線程本地的內(nèi)存分配,而不需要顧及分區(qū)是否連續(xù)。因此,每個應(yīng)用線程和GC線程都會獨立的使用分區(qū)校辩,進而減少同步時間辆童,提升GC效率宜咒,這個分區(qū)稱為本地分配緩沖區(qū)(Lab)。

其中把鉴,應(yīng)用線程可以獨占一個本地緩沖區(qū)(TLAB)來創(chuàng)建的對象故黑,而大部分都會落入Eden區(qū)域(巨型對象或分配失敗除外),因此TLAB的分區(qū)屬于Eden空間;而每次垃圾收集時场晶,每個GC線程同樣可以獨占一個本地緩沖區(qū)(GCLAB)用來轉(zhuǎn)移對象混埠,每次回收會將對象復(fù)制到Suvivor空間或老年代空間;對于從Eden/Survivor空間晉升(Promotion)到Survivor/老年代空間的對象诗轻,同樣有GC獨占的本地緩沖區(qū)進行操作钳宪,該部分稱為晉升本地緩沖區(qū)(PLAB)。

2.2.3 分區(qū)模型

image.png

G1對內(nèi)存的使用以Region為單位概耻, 對對象的分配以Card為單位使套。
1)巨型對象Humongous Region
一個大小達到一半甚至超過分區(qū)大小一半的對象稱之為巨型對象。當(dāng)線程為巨型對象分配空間時鞠柄,不能在tlab 分配侦高, 因為移動成本很高, 巨型對象可能跨多個分區(qū)厌杜。因此奉呛,巨型對象直接會在老年代分配,所占用連續(xù)空間稱之為巨型分區(qū)夯尽。G1內(nèi)部做了一個優(yōu)化瞧壮,一旦發(fā)現(xiàn)沒有引用指向巨型對象, 則可以直接在年輕代回收中回收匙握。

巨型對象會獨占一個咆槽、多個連續(xù)分區(qū),其中第一個分區(qū)被標(biāo)記為開始巨型圈纺, 相鄰連續(xù)分區(qū)被標(biāo)記為連續(xù)巨型秦忿。由于無法享受Lab帶來的優(yōu)化,并且確定一片連續(xù)的內(nèi)存空間需要掃描整堆蛾娶,因此確定巨型對象開始位置的成本非常高灯谣,如果可以,應(yīng)用程序應(yīng)避免生成巨型對象蛔琅。

2) 已記憶集合Remember Set (RSet)

在串行和并行收集器中胎许,GC通過整堆掃描,來確定對象是否處于可達路徑中罗售。然而G1為了避免STW式的整堆掃描辜窑,在每個分區(qū)記錄了一個已記憶集合(RSet),內(nèi)部類似一個反向指針寨躁,記錄引用分區(qū)內(nèi)對象的卡片索引谬擦。當(dāng)要回收該分區(qū)時,通過掃描分區(qū)的RSet朽缎,來確定引用本分區(qū)內(nèi)的對象是否存活惨远,進而確定本分區(qū)內(nèi)的對象存活情況谜悟。

事實上,并非所有的引用都需要記錄在RSet中北秽,如果一個分區(qū)確定需要掃描葡幸,那么無需RSet也可以無遺漏的得到引用關(guān)系。那么引用源自本分區(qū)的對象贺氓,當(dāng)然不用落入RSet中蔚叨;同時,G1 GC每次都會對年輕代進行整體收集辙培,因此引用源自年輕代的對象蔑水,也不需要在RSet中記錄。最后只有老年代的分區(qū)可能會有RSet記錄扬蕊,這些分區(qū)稱為擁有RSet分區(qū)(an RSet’s owning region)搀别。

3)Per Region Table (PRT)

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

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

由上可知翎冲,粗粒度的PRT只是記錄了引用數(shù)量垂睬,需要通過整堆掃描才能找出所有引用,因此掃描速度也是最慢的抗悍。

2.2.4羔飞、 收集集合 (CSet)

image.png

收集集合(CSet)代表每次GC暫停時回收的一系列目標(biāo)分區(qū)。在任意一次收集暫停中檐春,CSet所有分區(qū)都會被釋放,內(nèi)部存活的對象都會被轉(zhuǎn)移到分配的空閑分區(qū)中么伯。因此無論是年輕代收集疟暖,還是混合收集,工作的機制都是一致的田柔。年輕代收集CSet只容納年輕代分區(qū)俐巴,而混合收集會通過啟發(fā)式算法,在老年代候選回收分區(qū)中硬爆,篩選出回收收益最高的分區(qū)添加到CSet中欣舵。

候選老年代分區(qū)的CSet準(zhǔn)入條件,可以通過活躍度閾值-XX:G1MixedGCLiveThresholdPercent(默認(rèn)85%)進行設(shè)置缀磕,從而攔截那些回收開銷巨大的對象缘圈;同時劣光,每次混合收集可以包含候選老年代分區(qū),可根據(jù)CSet對堆的總大小占比-XX:G1OldCSetRegionThresholdPercent(默認(rèn)10%)設(shè)置數(shù)量上限糟把。

由上述可知绢涡,G1的收集都是根據(jù)CSet進行操作的,年輕代收集與混合收集沒有明顯的不同遣疯,最大的區(qū)別在于兩種收集的觸發(fā)條件雄可。

1)年輕代收集集合 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é)維護對象的年齡(存活次數(shù))赏殃,輔助判斷老化(tenuring)對象晉升的時候是到Survivor分區(qū)還是到老年代分區(qū)。年輕代收集首先先將晉升對象尺寸總和间涵、對象年齡信息維護到年齡表中仁热,再根據(jù)年齡表、Survivor尺寸勾哩、Survivor填充容量-XX:TargetSurvivorRatio(默認(rèn)50%)抗蠢、最大任期閾值-XX:MaxTenuringThreshold(默認(rèn)15),計算出一個恰當(dāng)?shù)娜纹陂撝邓祭停彩浅^任期閾值的對象都會被晉升到老年代迅矛。

2)混合收集集合 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)收集達到參數(shù)時特纤,不再啟動新的混合收集军俊。而每次添加到CSet的分區(qū),則通過計算得到的GC效率進行安排捧存。

3)并發(fā)標(biāo)記算法(三色標(biāo)記法)
CMS和G1在并發(fā)標(biāo)記時使用的是同一個算法:三色標(biāo)記法粪躬,使用白灰黑三種顏色標(biāo)記對象。白色是未標(biāo)記昔穴;灰色自身被標(biāo)記镰官,引用的對象未標(biāo)記;黑色自身與引用對象都已標(biāo)記吗货。

image.png

GC 開始前所有對象都是白色泳唠,GC 一開始所有根能夠直達的對象被壓到棧中,待搜索宙搬,此時顏色是灰色笨腥。然后灰色對象依次從棧中取出搜索子對象,子對象也會被涂為灰色勇垛,入棧脖母。當(dāng)其所有的子對象都涂為灰色之后該對象被涂為黑色。當(dāng) GC 結(jié)束之后灰色對象將全部沒了闲孤,剩下黑色的為存活對象谆级,白色的為垃圾。

image.png

4)漏標(biāo)問題
在remark過程中讼积,黑色指向了白色肥照,如果不對黑色重新掃描,則會漏標(biāo)勤众。會把白色D對象當(dāng)作沒有新引用指向從而回收掉舆绎。

image.png

并發(fā)標(biāo)記過程中,Mutator刪除了所有從灰色到白色的引用们颜,會產(chǎn)生漏標(biāo)吕朵。此時白色對象應(yīng)該被回收

產(chǎn)生漏標(biāo)問題的條件有兩個:

  • 黑色對象指向了白色對象
  • 灰色對象指向白色對象的引用消失

所以要解決漏標(biāo)問題,打破兩個條件之一即可:
著作權(quán)歸https://pdai.tech所有掌桩。 鏈接:https://www.pdai.tech/md/java/jvm/java-jvm-gc-g1.html

  • 跟蹤黑指向白的增加 incremental update:增量更新,關(guān)注引用的增加姑食,把黑色重新標(biāo)記為灰色波岛,下次重新掃描屬性。CMS采用該方法音半。
  • 記錄灰指向白的消失 SATB snapshot at the beginning:關(guān)注引用的刪除则拷,當(dāng)灰–>白消失時贡蓖,要把這個 引用 推到GC的堆棧,保證白還能被GC掃描到煌茬。G1采用該方法斥铺。

為什么G1采用SATB而不用incremental update?

因為采用incremental update把黑色重新標(biāo)記為灰色后坛善,之前掃描過的還要再掃描一遍晾蜘,效率太低。G1有RSet與SATB相配合眠屎。Card Table里記錄了RSet剔交,RSet里記錄了其他對象指向自己的引用,這樣就不需要再掃描其他區(qū)域改衩,只要掃描RSet就可以了岖常。

也就是說 灰色–>白色 引用消失時,如果沒有 黑色–>白色葫督,引用會被push到堆棧竭鞍,下次掃描時拿到這個引用,由于有RSet的存在橄镜,不需要掃描整個堆去查找指向白色的引用偎快,效率比較高。SATB配合RSet渾然天成蛉鹿。

2.3滨砍、G1的活動周期

2.3.1、G1垃圾收集活動匯總

G1垃圾收集活動周期圖


image.png

2.3.2妖异、 RSet的維護

由于不能整堆掃描惋戏,又需要計算分區(qū)確切的活躍度,因此他膳,G1需要一個增量式的完全標(biāo)記并發(fā)算法响逢,通過維護RSet,得到準(zhǔn)確的分區(qū)引用信息棕孙。在G1中舔亭,RSet的維護主要來源兩個方面:寫柵欄(Write Barrier)和并發(fā)優(yōu)化線程(Concurrence Refinement Threads)

2.3.2.1、 柵欄Barrier

柵欄代碼示意

image.png

我們首先介紹一下柵欄(Barrier)的概念蟀俊。柵欄是指在原生代碼片段中钦铺,當(dāng)某些語句被執(zhí)行時,柵欄代碼也會被執(zhí)行肢预。而G1主要在賦值語句中矛洞,使用寫前柵欄(Pre-Write Barrrier)和寫后柵欄(Post-Write Barrrier)。事實上烫映,寫柵欄的指令序列開銷非常昂貴沼本,應(yīng)用吞吐量也會根據(jù)柵欄復(fù)雜度而降低噩峦。

寫前柵欄 Pre-Write Barrrier

即將執(zhí)行一段賦值語句時,等式左側(cè)對象將修改引用到另一個對象抽兆,那么等式左側(cè)對象原先引用的對象所在分區(qū)將因此喪失一個引用识补,那么JVM就需要在賦值語句生效之前,記錄喪失引用的對象辫红。JVM并不會立即維護RSet凭涂,而是通過批量處理,在將來RSet更新(見SATB)厉熟。

寫后柵欄 Post-Write Barrrier

當(dāng)執(zhí)行一段賦值語句后导盅,等式右側(cè)對象獲取了左側(cè)對象的引用,那么等式右側(cè)對象所在分區(qū)的RSet也應(yīng)該得到更新揍瑟。同樣為了降低開銷白翻,寫后柵欄發(fā)生后,RSet也不會立即更新绢片,同樣只是記錄此次更新日志滤馍,在將來批量處理(見Concurrence Refinement Threads)。

2.3.2.2底循、 起始快照算法Snapshot at the beginning (SATB)

Taiichi Tuasa貢獻的增量式完全并發(fā)標(biāo)記算法起始快照算法(SATB)巢株,主要針對標(biāo)記-清除垃圾收集器的并發(fā)標(biāo)記階段,非常適合G1的分區(qū)塊的堆結(jié)構(gòu)熙涤,同時解決了CMS的主要煩惱:重新標(biāo)記暫停時間長帶來的潛在風(fēng)險阁苞。

SATB會創(chuàng)建一個對象圖,相當(dāng)于堆的邏輯快照祠挫,從而確保并發(fā)標(biāo)記階段所有的垃圾對象都能通過快照被鑒別出來那槽。當(dāng)賦值語句發(fā)生時,應(yīng)用將會改變了它的對象圖等舔,那么JVM需要記錄被覆蓋的對象骚灸。因此寫前柵欄會在引用變更前,將值記錄在SATB日志或緩沖區(qū)中慌植。每個線程都會獨占一個SATB緩沖區(qū)甚牲,初始有256條記錄空間。當(dāng)空間用盡時蝶柿,線程會分配新的SATB緩沖區(qū)繼續(xù)使用丈钙,而原有的緩沖去則加入全局列表中。最終在并發(fā)標(biāo)記階段交汤,并發(fā)標(biāo)記線程(Concurrent Marking Threads)在標(biāo)記的同時雏赦,還會定期檢查和處理全局緩沖區(qū)列表的記錄,然后根據(jù)標(biāo)記位圖分片的標(biāo)記位,掃描引用字段來更新RSet喉誊。此過程又稱為并發(fā)標(biāo)記/SATB寫前柵欄。

2.3.2.3纵顾、 并發(fā)優(yōu)化線程Concurrence Refinement Threads

G1中使用基于Urs H?lzle的快速寫柵欄伍茄,將柵欄開銷縮減到2個額外的指令。柵欄將會更新一個card table type的結(jié)構(gòu)來跟蹤代間引用施逾。

當(dāng)賦值語句發(fā)生后敷矫,寫后柵欄會先通過G1的過濾技術(shù)判斷是否是跨分區(qū)的引用更新,并將跨分區(qū)更新對象的卡片加入緩沖區(qū)序列汉额,即更新日志緩沖區(qū)或臟卡片隊列曹仗。與SATB類似,一旦日志緩沖區(qū)用盡蠕搜,則分配一個新的日志緩沖區(qū)怎茫,并將原來的緩沖區(qū)加入全局列表中。

并發(fā)優(yōu)化線程(Concurrence Refinement Threads)妓灌,只專注掃描日志緩沖區(qū)記錄的卡片來維護更新RSet轨蛤,線程最大數(shù)目可通過-XX:G1ConcRefinementThreads(默認(rèn)等于-XX:ParellelGCThreads)設(shè)置。并發(fā)優(yōu)化線程永遠(yuǎn)是活躍的虫埂,一旦發(fā)現(xiàn)全局列表有記錄存在祥山,就開始并發(fā)處理。如果記錄增長很快或者來不及處理掉伏,那么通過閾值-X:G1ConcRefinementGreenZone/-XX:G1ConcRefinementYellowZone/-XX:G1ConcRefinementRedZone缝呕,G1會用分層的方式調(diào)度,使更多的線程處理全局列表斧散。如果并發(fā)優(yōu)化線程也不能跟上緩沖區(qū)數(shù)量供常,則Mutator線程(Java應(yīng)用線程)會掛起應(yīng)用并被加進來幫助處理,直到全部處理完颅湘。因此话侧,必須避免此類場景出現(xiàn)。

2.3.3闯参、 并發(fā)標(biāo)記周期 Concurrent Marking Cycle

并發(fā)標(biāo)記周期是G1中非常重要的階段瞻鹏,這個階段將會為混合收集周期識別垃圾最多的老年代分區(qū)。整個周期完成根標(biāo)記鹿寨、識別所有(可能)存活對象新博,并計算每個分區(qū)的活躍度,從而確定GC效率等級脚草。

當(dāng)達到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ā)了幾次年輕代收集。

2.3.3.1给僵、 并發(fā)標(biāo)記線程 Concurrent Marking Threads

并發(fā)標(biāo)記位圖過程


image.png

要標(biāo)記存活的對象毫捣,每個分區(qū)都需要創(chuàng)建位圖(Bitmap)信息來存儲標(biāo)記數(shù)據(jù),來確定標(biāo)記周期內(nèi)被分配的對象帝际。G1采用了兩個位圖Previous Bitmap培漏、Next Bitmap,來存儲標(biāo)記數(shù)據(jù)胡本,Previous位圖存儲上次的標(biāo)記數(shù)據(jù)牌柄,Next位圖在標(biāo)記周期內(nèi)不斷變化更新,同時Previous位圖的標(biāo)記數(shù)據(jù)也越來越過時侧甫,當(dāng)標(biāo)記周期結(jié)束后Next位圖便替換Previous位圖珊佣,成為上次標(biāo)記的位圖。同時披粟,每個分區(qū)通過頂部開始標(biāo)記(TAMS)咒锻,來記錄已標(biāo)記過的內(nèi)存范圍。同樣的守屉,G1使用了兩個頂部開始標(biāo)記Previous TAMS(PTAMS)惑艇、Next TAMS(NTAMS),記錄已標(biāo)記的范圍拇泛。

在并發(fā)標(biāo)記階段滨巴,G1會根據(jù)參數(shù)-XX:ConcGCThreads(默認(rèn)GC線程數(shù)的1/4,即-XX:ParallelGCThreads/4)俺叭,分配并發(fā)標(biāo)記線程(Concurrent Marking Threads)恭取,進行標(biāo)記活動。每個并發(fā)線程一次只掃描一個分區(qū)熄守,并通過"手指"指針的方式優(yōu)化獲取分區(qū)蜈垮。并發(fā)標(biāo)記線程是爆發(fā)式的耗跛,在給定的時間段拼命干活,然后休息一段時間攒发,再拼命干活调塌。

每個并發(fā)標(biāo)記周期,在初始標(biāo)記STW的最后惠猿,G1會分配一個空的Next位圖和一個指向分區(qū)頂部(Top)的NTAMS標(biāo)記烟阐。Previous位圖記錄的上次標(biāo)記數(shù)據(jù),上次的標(biāo)記位置紊扬,即PTAMS,在PTAMS與分區(qū)底部(Bottom)的范圍內(nèi)唉擂,所有的存活對象都已被標(biāo)記餐屎。那么,在PTAMS與Top之間的對象都將是隱式存活(Implicitly Live)對象玩祟。在并發(fā)標(biāo)記階段腹缩,Next位圖吸收了Previous位圖的標(biāo)記數(shù)據(jù),同時每個分區(qū)都會有新的對象分配空扎,則Top與NTAMS分離藏鹊,前往更高的地址空間。在并發(fā)標(biāo)記的一次標(biāo)記中转锈,并發(fā)標(biāo)記線程將找出NTAMS與PTAMS之間的所有存活對象盘寡,將標(biāo)記數(shù)據(jù)存儲在Next位圖中。同時撮慨,在NTAMS與Top之間的對象即成為已標(biāo)記對象竿痰。如此不斷地更新Next位圖信息,并在清除階段與Previous位圖交換角色砌溺。

2.3.3.2影涉、 初始標(biāo)記 Initial Mark

初始標(biāo)記(Initial Mark)負(fù)責(zé)標(biāo)記所有能被直接可達的根對象(原生棧對象、全局對象规伐、JNI對象)蟹倾,根是對象圖的起點,因此初始標(biāo)記需要將Mutator線程(Java應(yīng)用線程)暫停掉猖闪,也就是需要一個STW的時間段鲜棠。事實上,當(dāng)達到IHOP閾值時培慌,G1并不會立即發(fā)起并發(fā)標(biāo)記周期岔留,而是等待下一次年輕代收集,利用年輕代收集的STW時間段检柬,完成初始標(biāo)記献联,這種方式稱為借道(Piggybacking)竖配。在初始標(biāo)記暫停中,分區(qū)的NTAMS都被設(shè)置到分區(qū)頂部Top里逆,初始標(biāo)記是并發(fā)執(zhí)行进胯,直到所有的分區(qū)處理完。

2.3.3.3原押、 根分區(qū)掃描 Root Region Scanning

在初始標(biāo)記暫停結(jié)束后胁镐,年輕代收集也完成的對象復(fù)制到Survivor的工作,應(yīng)用線程開始活躍起來诸衔。此時為了保證標(biāo)記算法的正確性盯漂,所有新復(fù)制到Survivor分區(qū)的對象,都需要被掃描并標(biāo)記成根笨农,這個過程稱為根分區(qū)掃描(Root Region Scanning)就缆,同時掃描的Suvivor分區(qū)也被稱為根分區(qū)(Root Region)。根分區(qū)掃描必須在下一次年輕代垃圾收集啟動前完成(并發(fā)標(biāo)記的過程中谒亦,可能會被若干次年輕代垃圾收集打斷)竭宰,因為每次GC會產(chǎn)生新的存活對象集合。

2.3.3.4份招、 并發(fā)標(biāo)記 Concurrent Marking

和應(yīng)用線程并發(fā)執(zhí)行切揭,并發(fā)標(biāo)記線程在并發(fā)標(biāo)記階段啟動,由參數(shù)-XX:ConcGCThreads(默認(rèn)GC線程數(shù)的1/4锁摔,即-XX:ParallelGCThreads/4)控制啟動數(shù)量廓旬,每個線程每次只掃描一個分區(qū),從而標(biāo)記出存活對象圖谐腰。在這一階段會處理Previous/Next標(biāo)記位圖嗤谚,掃描標(biāo)記對象的引用字段。同時怔蚌,并發(fā)標(biāo)記線程還會定期檢查和處理STAB全局緩沖區(qū)列表的記錄很钓,更新對象引用信息九默。參數(shù)-XX:+ClassUnloadingWithConcurrentMark會開啟一個優(yōu)化绷跑,如果一個類不可達(不是對象不可達)力奋,則在重新標(biāo)記階段,這個類就會被直接卸載籍胯。所有的標(biāo)記任務(wù)必須在堆滿前就完成掃描竟闪,如果并發(fā)標(biāo)記耗時很長,那么有可能在并發(fā)標(biāo)記過程中杖狼,又經(jīng)歷了幾次年輕代收集炼蛤。如果堆滿前沒有完成標(biāo)記任務(wù),則會觸發(fā)擔(dān)保機制蝶涩,經(jīng)歷一次長時間的串行Full GC理朋。

2.3.3.5絮识、 存活數(shù)據(jù)計算 Live Data Accounting

存活數(shù)據(jù)計算(Live Data Accounting)是標(biāo)記操作的附加產(chǎn)物,只要一個對象被標(biāo)記嗽上,同時會被計算字節(jié)數(shù)次舌,并計入分區(qū)空間。只有NTAMS以下的對象會被標(biāo)記和計算兽愤,在標(biāo)記周期的最后彼念,Next位圖將被清空,等待下次標(biāo)記周期浅萧。

2.3.3.6逐沙、 重新標(biāo)記 Remark

重新標(biāo)記(Remark)是最后一個標(biāo)記階段。在該階段中洼畅,G1需要一個暫停的時間吩案,去處理剩下的SATB日志緩沖區(qū)和所有更新,找出所有未被訪問的存活對象土思,同時安全完成存活數(shù)據(jù)計算。這個階段也是并行執(zhí)行的忆嗜,通過參數(shù)-XX:ParallelGCThread可設(shè)置GC暫停時可用的GC線程數(shù)己儒。同時,引用處理也是重新標(biāo)記階段的一部分捆毫,所有重度使用引用對象(弱引用闪湾、軟引用、虛引用绩卤、最終引用)的應(yīng)用都會在引用處理上產(chǎn)生開銷途样。

2.3.3.7、 清除 Cleanup

緊挨著重新標(biāo)記階段的清除(Clean)階段也是STW的濒憋。Previous/Next標(biāo)記位圖何暇、以及PTAMS/NTAMS,都會在清除階段交換角色凛驮。清除階段主要執(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ū)可在清除階段直接回收婚惫,無需等待下次收集周期氛赐。

2.3.4魂爪、 年輕代收集/混合收集周期

年輕代收集和混合收集周期,是G1回收空間的主要活動鹰祸。當(dāng)應(yīng)用運行開始時甫窟,堆內(nèi)存可用空間還比較大,只會在年輕代滿時蛙婴,觸發(fā)年輕代收集粗井;隨著老年代內(nèi)存增長,當(dāng)?shù)竭_IHOP閾值-XX:InitiatingHeapOccupancyPercent(老年代占整堆比街图,默認(rèn)45%)時浇衬,G1開始著手準(zhǔn)備收集老年代空間。首先經(jīng)歷并發(fā)標(biāo)記周期餐济,識別出高收益的老年代分區(qū)耘擂,前文已述。但隨后G1并不會馬上開始一次混合收集絮姆,而是讓應(yīng)用線程先運行一段時間醉冤,等待觸發(fā)一次年輕代收集。在這次STW中篙悯,G1將保準(zhǔn)整理混合收集周期蚁阳。接著再次讓應(yīng)用線程運行,當(dāng)接下來的幾次年輕代收集時鸽照,將會有老年代分區(qū)加入到CSet中螺捐,即觸發(fā)混合收集,這些連續(xù)多次的混合收集稱為混合收集周期(Mixed Collection Cycle)矮燎。

2.3.4.1定血、 GC工作線程數(shù)

GC工作線程數(shù) -XX:ParallelGCThreads

JVM可以通過參數(shù)-XX:ParallelGCThreads進行指定GC工作的線程數(shù)量。參數(shù)-XX:ParallelGCThreads默認(rèn)值并不是固定的诞外,而是根據(jù)當(dāng)前的CPU資源進行計算澜沟。如果用戶沒有指定,且CPU小于等于8峡谊,則默認(rèn)與CPU核數(shù)相等倔喂;若CPU大于8,則默認(rèn)JVM會經(jīng)過計算得到一個小于CPU核數(shù)的線程數(shù)靖苇;當(dāng)然也可以人工指定與CPU核數(shù)相等席噩。

2.3.4.2、 年輕代收集 Young Collection

每次收集過程中贤壁,既有并行執(zhí)行的活動悼枢,也有串行執(zhí)行的活動,但都可以是多線程的脾拆。在并行執(zhí)行的任務(wù)中馒索,如果某個任務(wù)過重莹妒,會導(dǎo)致其他線程在等待某項任務(wù)的處理,需要對這些地方進行優(yōu)化绰上。

并行活動

  • 外部根分區(qū)掃描 Ext Root Scanning:此活動對堆外的根(JVM系統(tǒng)目錄旨怠、VM數(shù)據(jù)結(jié)構(gòu)、JNI線程句柄蜈块、硬件寄存器鉴腻、全局變量、線程對棧根)進行掃描百揭,發(fā)現(xiàn)那些沒有加入到暫停收集集合CSet中的對象爽哎。如果系統(tǒng)目錄(單根)擁有大量加載的類,最終可能其他并行活動結(jié)束后器一,該活動依然沒有結(jié)束而帶來的等待時間课锌。
  • 更新已記憶集合 Update RS:并發(fā)優(yōu)化線程會對臟卡片的分區(qū)進行掃描更新日志緩沖區(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后進行一次統(tǒng)計
  • 代碼根掃描 Code Root Scanning:對代碼根集合進行掃描暖释,掃描JVM編譯后代碼Native Method的引用信息(nmethod掃描)袭厂,進行RSet掃描。事實上球匕,只有CSet分區(qū)中的RSet有強代碼根時纹磺,才會做nmethod掃描,查找對CSet的引用亮曹。
  • 轉(zhuǎn)移和回收 Object Copy:通過選定的CSet以及CSet分區(qū)完整的引用集橄杨,將執(zhí)行暫停時間的主要部分:CSet分區(qū)存活對象的轉(zhuǎn)移、CSet分區(qū)空間的回收乾忱。通過工作竊取機制來負(fù)載均衡地選定復(fù)制對象的線程讥珍,并且復(fù)制和掃描對象被轉(zhuǎn)移的存活對象將拷貝到每個GC線程分配緩沖區(qū)GCLAB。G1會通過計算窄瘟,預(yù)測分區(qū)復(fù)制所花費的時間衷佃,從而調(diào)整年輕代的尺寸。
  • 終止 Termination:完成上述任務(wù)后蹄葱,如果任務(wù)隊列已空氏义,則工作線程會發(fā)起終止要求。如果還有其他線程繼續(xù)工作图云,空閑的線程會通過工作竊取機制嘗試幫助其他線程處理惯悠。而單獨執(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摹恨,掃描時會在全局卡片表中進行標(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)了一個不可達的巨型對象,該對象分區(qū)會被回收喻粹。
  • 釋放分區(qū) Free CSet:回收CSet分區(qū)的所有空間蟆融,并加入到空閑分區(qū)中。
  • 其他活動 Other:GC中可能還會經(jīng)歷其他耗時很小的活動守呜,如修復(fù)JNI句柄等型酥。

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

當(dāng)G1發(fā)起并發(fā)標(biāo)記周期之后查乒,并不會馬上開始混合收集弥喉。G1會先等待下一次年輕代收集,然后在該收集階段中侣颂,確定下次混合收集的CSet(Choose CSet)档桃。

2.3.5.1枪孩、 混合收集周期 Mixed Collection Cycle

單次的混合收集與年輕代收集并無二致憔晒。根據(jù)暫停目標(biāo),老年代的分區(qū)可能不能一次暫停收集中被處理完蔑舞,G1會發(fā)起連續(xù)多次的混合收集拒担,稱為混合收集周期(Mixed Collection Cycle)。G1會計算每次加入到CSet中的分區(qū)數(shù)量攻询、混合收集進行次數(shù)从撼,并且在上次的年輕代收集、以及接下來的混合收集中,G1會確定下次加入CSet的分區(qū)集(Choose CSet)低零,并且確定是否結(jié)束混合收集周期婆翔。

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

轉(zhuǎn)移失敗(Evacuation Failure)是指當(dāng)G1無法在堆空間中申請新的分區(qū)時掏婶,G1便會觸發(fā)擔(dān)保機制啃奴,執(zhí)行一次STW式的、單線程的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ū)
  • 分配巨型對象時在老年代無法找到足夠的連續(xù)分區(qū)

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

2.4雀费、 總結(jié)

G1是一款非常優(yōu)秀的垃圾收集器,不僅適合堆內(nèi)存大的應(yīng)用痊焊,同時也簡化了調(diào)優(yōu)的工作盏袄。通過主要的參數(shù)初始和最大堆空間、以及最大容忍的GC暫停目標(biāo)薄啥,就能得到不錯的性能辕羽;同時,我們也看到G1對內(nèi)存空間的浪費較高垄惧,但通過首先收集盡可能多的垃圾(Garbage First)的設(shè)計原則刁愿,可以及時發(fā)現(xiàn)過期對象,從而讓內(nèi)存占用處于合理的水平到逊。

雖然G1也有類似CMS的收集動作:初始標(biāo)記铣口、并發(fā)標(biāo)記、重新標(biāo)記觉壶、清除脑题、轉(zhuǎn)移回收,并且也以一個串行收集器做擔(dān)保機制铜靶,但單純地以類似前三種的過程描述顯得并不是很妥當(dāng)叔遂。

  • G1的設(shè)計原則是"首先收集盡可能多的垃圾(Garbage First)"。因此,G1并不會等內(nèi)存耗盡(串行已艰、并行)或者快耗盡(CMS)的時候開始垃圾收集痊末,而是在內(nèi)部采用了啟發(fā)式算法,在老年代找出具有高收集收益的分區(qū)進行收集哩掺。同時G1可以根據(jù)用戶設(shè)置的暫停時間目標(biāo)自動調(diào)整年輕代和總堆大小舌胶,暫停目標(biāo)越短年輕代空間越小、總空間就越大疮丛;
  • G1采用內(nèi)存分區(qū)(Region)的思路幔嫂,將內(nèi)存劃分為一個個相等大小的內(nèi)存分區(qū),回收時則以分區(qū)為單位進行回收誊薄,存活的對象復(fù)制到另一個空閑分區(qū)中履恩。由于都是以相等大小的分區(qū)為單位進行操作,因此G1天然就是一種壓縮方案(局部壓縮)呢蔫;
  • G1雖然也是分代收集器切心,但整個內(nèi)存分區(qū)不存在物理上的年輕代與老年代的區(qū)別,也不需要完全獨立的survivor(to space)堆做復(fù)制準(zhǔn)備片吊。G1只有邏輯上的分代概念绽昏,或者說每個分區(qū)都可能隨G1的運行在不同代之間前后切換;
  • G1的收集都是STW的俏脊,但年輕代和老年代的收集界限比較模糊全谤,采用了混合(mixed)收集的方式。即每次收集既可能只收集年輕代分區(qū)(年輕代收集)爷贫,也可能在收集年輕代的同時认然,包含部分老年代分區(qū)(混合收集),這樣即使堆內(nèi)存很大時漫萄,也可以限制收集范圍卷员,從而降低停頓。

3腾务、參考資料

https://www.cnblogs.com/webor2006/p/11110263.html
https://www.pdai.tech/md/java/jvm/java-jvm-gc-g1.html
https://zhuanlan.zhihu.com/p/59861022

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毕骡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子岩瘦,更是在濱河造成了極大的恐慌未巫,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件担钮,死亡現(xiàn)場離奇詭異橱赠,居然都是意外死亡尤仍,警方通過查閱死者的電腦和手機箫津,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苏遥,你說我怎么就攤上這事饼拍。” “怎么了田炭?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵师抄,是天一觀的道長。 經(jīng)常有香客問我教硫,道長叨吮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任瞬矩,我火速辦了婚禮茶鉴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘景用。我一直安慰自己涵叮,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布伞插。 她就那樣靜靜地躺著割粮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪媚污。 梳的紋絲不亂的頭發(fā)上舀瓢,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音耗美,去河邊找鬼氢伟。 笑死,一個胖子當(dāng)著我的面吹牛幽歼,可吹牛的內(nèi)容都是我干的朵锣。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼甸私,長吁一口氣:“原來是場噩夢啊……” “哼诚些!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起皇型,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤诬烹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后弃鸦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绞吁,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年唬格,在試婚紗的時候發(fā)現(xiàn)自己被綠了家破。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颜说。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖汰聋,靈堂內(nèi)的尸體忽然破棺而出门粪,到底是詐尸還是另有隱情,我是刑警寧澤烹困,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布玄妈,位于F島的核電站,受9級特大地震影響髓梅,放射性物質(zhì)發(fā)生泄漏拟蜻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一枯饿、第九天 我趴在偏房一處隱蔽的房頂上張望瞭郑。 院中可真熱鬧,春花似錦鸭你、人聲如沸屈张。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阁谆。三九已至,卻和暖如春愉老,著一層夾襖步出監(jiān)牢的瞬間场绿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工嫉入, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留焰盗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓咒林,卻偏偏與公主長得像熬拒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子垫竞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容