摘要:垃圾收集器簡述全文共兩部分有基礎(chǔ)的讀者只需要閱讀第一部分垃圾收集器在最新幾個版本的發(fā)展第二部分為基礎(chǔ)部分垃圾收集器在最新幾個版本的發(fā)展垃圾收集器始見于版本在后續(xù)的幾個版本中對它進(jìn)行了優(yōu)化和改進(jìn)在中垃圾收集器增加了幾個可配置選項(xiàng)的自動發(fā)現(xiàn)功能
G1垃圾收集器簡述
全文共兩部分,有基礎(chǔ)的讀者只需要閱讀第一部分"G1垃圾收集器在最新幾個版本的發(fā)展",第二部分為基礎(chǔ)部分.
G1垃圾收集器在最新幾個版本的發(fā)展
G1垃圾收集器始見于1.7版本,在后續(xù)的幾個版本中對它進(jìn)行了優(yōu)化和改進(jìn):在JAVA9中,G1垃圾收集器增加了幾個可配置選項(xiàng)的自動發(fā)現(xiàn)功能,同時它被設(shè)置為默認(rèn)的垃圾收集器(32/64位服務(wù)器),取代了Parallel gc,同時deprecated了cms;在JAVA10中,G1的FULL GC被改進(jìn)為并行以縮短時間;在JAVA11中,g1在處理Reference時的線程數(shù)支持自適應(yīng)調(diào)整,也是同時在這一版,所有g(shù)c在stw階段均支持了自適應(yīng)的并行度調(diào)整.
截止此文時,最新版的JAVA12在推出Shenandoah GC,對zgc支持并發(fā)類卸載的同時,依舊對G1垃圾收集器進(jìn)行了幾點(diǎn)優(yōu)化:
對G1 和 Parallel GC推出的體驗(yàn)特性,支持NV-DIMM等可選設(shè)備上分配老年代(自JAVA10開始,堆內(nèi)存可以分配在NV-DIMM,關(guān)于DIMM的優(yōu)點(diǎn)本文不作論述).
如果啟用了這個功能,年輕代依舊使用DRAM安放,僅有老年代會存放在NV-DIMM.G1在任何一個給定的時間點(diǎn)保證提交到DRAM和NV-DIMM上的內(nèi)存永遠(yuǎn)會小于-Xms指定的內(nèi)存總量.目前最新的實(shí)現(xiàn)方式是將完整的JAVA堆預(yù)分配到NV-DIMM文件系統(tǒng),這樣可以避免動態(tài)的代擴(kuò)容,但是將保證NV-DIMM文件系統(tǒng)空間充足的責(zé)任甩給了用戶.啟用時,即使用戶顯式設(shè)置了年輕代大小,虛擬機(jī)同時也基于DRAM的總可用量對年輕代進(jìn)行了限定.
舉例說明:如果虛擬機(jī)在一個具備32G的DRAM和1024G的NV-DIMM內(nèi)存的系統(tǒng)上運(yùn)行,指定了-Xms756g,虛擬機(jī)會對年輕代進(jìn)行計(jì)算,并使用計(jì)算結(jié)果進(jìn)行限定.
如果未指定-XX:MaxNewSize或-Xmn,最大年輕代大小將設(shè)置為可用內(nèi)存的百分之八十(25.6G);指定了-XX:MaxNewSize 或 -Xmn,最大年輕代大小依舊以25.6G為封頂;使用-XX:MaxRAM可告訴虛擬機(jī)有多少DRAM可用,那么年輕代的大小設(shè)置為該參數(shù)指定的值的百分之八十;使用-XX:MaxRAMPercentage可指定DRAM中有多大的百分比對于年輕代可用(默認(rèn)百分之八十);
啟動時,可通過日志選項(xiàng)gc+ergo=info打印最大年輕代大小.
g1可在并發(fā)標(biāo)記周期釋放內(nèi)存.
在12版,G1默認(rèn)可以在并發(fā)標(biāo)記周期將應(yīng)用進(jìn)程不需要的空閑的堆內(nèi)存交回操作系統(tǒng),從而提升了java進(jìn)程對內(nèi)存的使用效率,若使用-Xms選項(xiàng)將初始內(nèi)存設(shè)置為最大內(nèi)存,則此功能會被禁用.
可終止的g1混合gc
G1有一個非常重要的目標(biāo):在gc停頓階段適配用戶期望的停頓時間.一直以來,G1選擇一段收集期內(nèi)完成的大量工作信息(一定程度上依賴于應(yīng)用行為本身)作為樣本進(jìn)行高度分析,分析后選定的一組分區(qū)被稱為collection set(簡稱cs,即回收集),一旦cs被選定并且G1開始了回收,那么G1必須不停頓地回收所有這些cs中的存活對象.這個行為可能會因?yàn)镚1的啟發(fā)式算法選擇了過大的cs而導(dǎo)致回收時間超過用戶設(shè)置的目標(biāo)停頓時間.應(yīng)用行為突變是一個典型的復(fù)現(xiàn)場景,它會造成啟發(fā)式算法依托于"臟"數(shù)據(jù),當(dāng)出現(xiàn)這種情況時,可以觀測到mix gc(關(guān)于mix gc可參考后面更基礎(chǔ)的描述)過程中包含了過多的老年代分區(qū),因此需要一個機(jī)制來發(fā)現(xiàn)G1的啟發(fā)式算法是否重復(fù)選擇了前面垃圾收集過程中的錯誤工作數(shù)據(jù),并在必要時讓G1增量地按小步運(yùn)行回收工作,每一個小步完成之后,回收工作都可以取消,通過這樣的機(jī)制,G1可更加容易地達(dá)到或者接近用戶指定的目標(biāo)停頓時間要求.
具體的過程:如果G1發(fā)現(xiàn)了啟發(fā)式算法重復(fù)選取了錯誤的分區(qū)數(shù),立即切換為一個更加精細(xì)的mix gc方式,首先,將cs分割成兩個部分,必選和可選.必選部分會包含cs中的g1不能細(xì)化處理的部分(如年輕代),但為了提升效率,它也可以包含部分老年代分區(qū).剩余的老年代分區(qū)便組成了可選的cs部分.
當(dāng)G1完成了必選部分的回收之后,如果有時間剩余,G1以更細(xì)粒度回收可選部分.回收cs可選部分的粒度取決于這個剩余時間,粒度最細(xì)化的情況下限定在一個分區(qū).在完成可選cs的任何一部分回收后,G1可以依照剩余時間來決定是否取消回收過程.
因?yàn)榱6鹊募?xì)化,G1對為達(dá)到停頓時間目標(biāo)而預(yù)算的cs變的更加精確,可選的cs會在整個過程中越來越小,最終結(jié)果是必選部分再一次包含了cs的所有分區(qū).如果某一刻啟發(fā)式算法的結(jié)果變的重新不精確起來,那么下一次回收將會重新包含"必選"和"可選".
讓g1在空閑時自動釋放已提交但未使用的內(nèi)存.
在此之前,G1不會定時地將堆中已提交的內(nèi)存釋放回操作系統(tǒng),它只會在full gc或并發(fā)周期內(nèi)做這件事,因?yàn)間1一直在努力避免full gc,并僅會基于java堆的占用和內(nèi)存分配活動來觸發(fā)一個并發(fā)周期,G1除非顯式強(qiáng)制要求,否則不會將堆內(nèi)存釋放.這個行為在付費(fèi)購買資源的容器化環(huán)境中是明顯的劣勢.即使在空閑時,虛擬機(jī)使用破碎的內(nèi)存資源時,G1也會持有全部的java堆,結(jié)果就是云用戶為這些空閑占用的資源進(jìn)行了額外的買單.
如果讓虛擬機(jī)有能力發(fā)現(xiàn)處于空閑態(tài)的java堆,自動在空閑時減少堆的使用,將會是一個大幅提升.當(dāng)然,Shenandoah 和 GenCon 收集器已經(jīng)支持了類似的功能.顯然的,對于web服務(wù)用戶來說,夜間的請求數(shù)量和白天的請求數(shù)量往往相差甚遠(yuǎn),服務(wù)器在白天頻繁地處理請求,而大部分夜晚卻處于空閑狀態(tài).當(dāng)然官方還是做出了相應(yīng)的調(diào)研,通過對實(shí)時的tomcat服務(wù)器的晝夜服務(wù)差距估計(jì),此解決方案可以減少虛擬機(jī)的85%的內(nèi)存提交量.
為了實(shí)現(xiàn)盡可能將無用內(nèi)存釋放回操作系統(tǒng)的目標(biāo),G1將會在應(yīng)用空閑時周期的嘗試觸發(fā)一個并發(fā)周期,這用以斷定java堆的全局使用結(jié)果,它將會導(dǎo)致java堆中的未使用部分自動地返回給操作系統(tǒng),當(dāng)然用戶可以選擇在這一個過程中使用full gc來最大化返回內(nèi)存.有兩種情況,G1會認(rèn)為應(yīng)用不活躍并觸發(fā)周期gc:第一是任何一次gc停斷后超過"G1PeriodicGCInterval"指定的毫秒數(shù)且這期間沒有任何正在進(jìn)行的并發(fā)周期.如果該值指定為0,則表示功能禁用;第二是由JVM調(diào)用宿主系統(tǒng)的getloadavg()方法返回的一分鐘平均系統(tǒng)負(fù)載值低于"G1PeriodicGCSystemLoadThreshold"時,但如果指定G1PeriodicGCSystemLoadThreshold為0也會禁用.如果兩個條件均不滿足,則取消相應(yīng)的周期gc,直到下一次G1PeriodicGCInterval滿足了為止.周期gc的類型是由選項(xiàng)G1PeriodicGCInvokesConcurrent決定的,如果設(shè)置了該選項(xiàng),G1會開啟一個并發(fā)周期來回收,否則會使用full GC.在每一次回收后G1都會調(diào)整當(dāng)前的堆大小,悄悄地把內(nèi)存還給操作系統(tǒng).新的java堆大小是由一些配置決定的,包含但不限于MinHeapFreeRatio,MaxHeapFreeRatio,最小最大堆大小配置.
為了避免擾動應(yīng)用進(jìn)程,G1默認(rèn)會在周期gc中間開始或繼續(xù)一個并發(fā)周期,但是相對于full gc,明顯不能釋放更多的內(nèi)存.
在相應(yīng)的gc日志中,由這個機(jī)制觸發(fā)的gc將會打上相應(yīng)的標(biāo)簽,詳見下面的例子.
(1) 6.084s[gc,periodic ] Checking for periodic GC.
[6.086s][info ][gc ] GC(13) Pause Young (Concurrent Start) (G1 Periodic Collection) 37M->36M(78M) 1.786ms
(2) 9.087s[gc,periodic ] Checking for periodic GC.
[9.088s][info ][gc ] GC(15) Pause Young (Prepare Mixed) (G1 Periodic Collection) 9M->9M(32M) 0.722ms
(3) 12.089s[gc,periodic ] Checking for periodic GC.
[12.091s][info ][gc ] GC(16) Pause Young (Mixed) (G1 Periodic Collection) 9M->5M(32M) 1.776ms
(4) 15.092s[gc,periodic ] Checking for periodic GC.
[15.097s][info ][gc ] GC(17) Pause Young (Mixed) (G1 Periodic Collection) 5M->1M(32M) 4.142ms
(5) 18.098s[gc,periodic ] Checking for periodic GC.
[18.100s][info ][gc ] GC(18) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 1.685ms
(6) 21.101s[gc,periodic ] Checking for periodic GC.
[21.102s][info ][gc ] GC(20) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.868ms
(7) 24.104s[gc,periodic ] Checking for periodic GC.
[24.104s][info ][gc ] GC(22) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.778ms
上面的例子指定了G1PeriodicGCInterval的值為3000ms,在(1)中應(yīng)用保持不活躍一段時間后,G1啟動了一個并發(fā)周期,標(biāo)記(Concurrent Start)和(G1 Periodic Collection).這次并發(fā)周期的啟動立即釋放了一些內(nèi)存,可以看到(1)到(2)的(78M)和(32M).在(2)到(4)觸發(fā)了更多的周期回收,這一次觸發(fā)的回收方式為mix gc并整理堆.緊隨的周期gc(5)到(7)僅開始了一個并發(fā)周期,因?yàn)镚1的策略判斷此時老年代的垃圾數(shù)量不足,不必開始mix gc.本例中,因?yàn)槎汛笮∫呀?jīng)保持到最小堆size,周期gc(5)到(7)不會進(jìn)一步壓縮堆內(nèi)存.
在應(yīng)用閑暇時間,對象存活狀態(tài)的改變(如軟引用過期)可能會觸發(fā)已提交java堆內(nèi)存的進(jìn)一步縮小.
G1垃圾收集器簡介
本節(jié)的一些數(shù)量指標(biāo)主要依托于最新JAVA12的文檔,有一些"量化"的數(shù)值可能與其他文章不一致.
按照官方文檔說法,"垃圾優(yōu)先"(G1 即garbage first)收集器專注于多處理器,大內(nèi)存的應(yīng)用場景.(這個大內(nèi)存似乎是在zgc等專注超大堆gc出現(xiàn)之前的描述)它試圖在少量配置前提下,滿足用戶指定的目標(biāo)停頓時間,同時保持一定的高吞吐量.G1旨在應(yīng)用延遲和吞吐量之間提供一個當(dāng)前應(yīng)用環(huán)境下的最佳平衡,這些典型適選G1的應(yīng)用環(huán)境的特征有:
堆大小高達(dá)數(shù)十G或者更大(在一些文章和問答中有6G的標(biāo)識,可能為舊版本),且存活數(shù)據(jù)可占用高達(dá)50%的堆內(nèi)存.
對象分配和晉升的速度可能會隨著時間推移大幅度改變.
堆中有大量的內(nèi)存碎片.
期望不長于幾百毫秒的可預(yù)測的停頓時間目標(biāo),避免長期的gc停頓.
G1已替換了cms垃圾收集器且是默認(rèn)的垃圾收集器.
基本概念
按照官方表述,G1是一個分代的,增變的,并行的,多數(shù)情況并發(fā)的,會stop-the-world的,"排泄/遷移"(evacuating)的垃圾收集器,專注于每次stop-the-world的停頓的停頓時間.這個"分代"其實(shí)是可選的,即也有無代模式."增變"特性在前面的新特性章節(jié)已經(jīng)闡述過.與其他垃圾收集器類似,G1也將堆劃分(虛擬的)為年輕代和老年代,同樣符合老規(guī)矩:年輕代的回收是最高效的,也是主要的工作,偶爾也伴隨一些老年代的空間回收.
在G1中,為了提升吞吐量,有一些操作永遠(yuǎn)是stop-the-world的.其他的一些要長期的,如全局標(biāo)記這種要全堆進(jìn)行的操作與應(yīng)用程序并發(fā)進(jìn)行.為了讓空間回收的stop-the-world停頓盡可能減少,G1并行的分步的遞增進(jìn)行空間回收.G1通過追蹤此前應(yīng)用行為和垃圾回收停頓的信息來構(gòu)建一個與開銷有關(guān)的模型.它使用這些信息去圍限停頓期間可做的工作.舉個例子,G1首先回收最高效的區(qū)域(也即垃圾最滿的區(qū)域,因此稱為垃圾-優(yōu)先).
G1使用"排泄"的方式回收大多空間,在選定的內(nèi)存區(qū)域,存活的對象被拷貝到新的區(qū)域,并在這個過程對他們進(jìn)行壓縮整理.在排泄完成后,存活對象之前占用的空間可以交給應(yīng)用程序重新使用, 即可用來分配新的對象.
堆布局
G1將堆分割成一組等大小的堆區(qū),一個區(qū)是內(nèi)存分配和回收的基本單元.在任何一個給定的時間,每一個區(qū)可以是空的(下圖淺灰色),也可以分配了特殊的代(年輕或老年).當(dāng)有內(nèi)存分配請求到來時,內(nèi)存管理者上交空閑的分區(qū),把它們指派給一個代并且交給應(yīng)用程序,作為應(yīng)用程序可自由分配的自由空間.
上圖中,純紅色為eden區(qū),標(biāo)有"S"的為幸存者區(qū),與此前其他的垃圾收集器功能保持一致,不同之處在于G1中這些同代的分區(qū)自身并不連續(xù).老年代分區(qū)由淺藍(lán)色表示,它的占用者可能會是跨多個分區(qū)的大型對象(帶有"H"),一般情況下,應(yīng)用程序會將對象分配到年輕代中的eden區(qū),大對象則直接分配到老年代.
gc周期
G1垃圾收集器宏觀上有兩個階段,它會在兩個階段之間往復(fù)切換.兩個階段分別是young-only階段和空間回收階段.young-only階段包含一系列逐漸充滿老年代可用空間的gc.空間回收階段,G1遞進(jìn)地回收老年代中的空間,同時也處理年輕代.緊接著,G1又重新進(jìn)入young-only階段,開始新一輪的循環(huán).
上圖表示G1的不同階段以及有關(guān)的停頓.可以看到圖上有一些實(shí)心的圓圈,每一個圈都表示一次gc停頓:藍(lán)色圓表示young-only回收停頓,橘黃色表示包含標(biāo)記過程的停頓,紅色表示混合gc的停頓.這些停頓用箭頭標(biāo)記了循環(huán)順序,從young-only進(jìn)入到混合gc,再回到y(tǒng)oung-only.young-only階段開始于若干個young-only gc,圖上由小藍(lán)圈表示,在幾次gc后,老年代中的對象占有超過了InitiatingHeapOccupancyPercent定義的閾值,則下一次的gc停頓將會初始化一個標(biāo)記gc的停頓,上圖用大藍(lán)圈表示,它除了標(biāo)記以外,其他的工作與young-only停頓一致,同時它會為并發(fā)標(biāo)記做出準(zhǔn)備.
當(dāng)運(yùn)行并發(fā)標(biāo)記時,其他young only停頓也可能會發(fā)生,直到remark停頓(第一個大黃圈)為止(作者認(rèn)為remark等階段不應(yīng)簡單地按字面意思翻譯,如直譯為重新標(biāo)記,但是這一階段做出的工作本身也不止如此,就如gc本意garbage collection/collector是垃圾回收/器的意思,卻不止負(fù)責(zé)回收,也影響內(nèi)存分配等,其他的階段也類似),在remark階段,G1完成標(biāo)記.直到Cleanup階段之前,仍舊可能會有額外的young-only gc.在Cleanup停頓之后,將會有一個最終的young-only gc終止整個young-only階段.在空間回收階段,會發(fā)生一系列的混合gc,上圖中用紅色圈表示,一般來講,它的數(shù)量會比young-only階段的young-only停頓少,因?yàn)镚1努力去使空間回收盡可能的高效.下面總結(jié)G1周期中的各個階段,階段中的停頓和轉(zhuǎn)換:
young-only階段:這一階段開始于幾個普通的young gc,它們會提升年輕代對象到老年代.young only階段與空間回收階段的轉(zhuǎn)換會在老年代的占有達(dá)到一個確定閾值后開始,這個閾值為初始堆占有閾(Initiating Heap Occupancy threshold),在此時刻,G1會調(diào)度一個并發(fā)開始的young gc并用它替換普通的young gc.
并發(fā)開始:它除了執(zhí)行普通的young gc外,還開始了標(biāo)記過程,它會并發(fā)地標(biāo)記所有老年代中當(dāng)前可達(dá)的存活對象以用于后續(xù)的空間回收階段.當(dāng)標(biāo)記未完成時,普通young gc可能也會穿插發(fā)生.標(biāo)記完成伴隨兩個特殊的停頓:Remark和Cleanup.
remark:此停頓會終止自身的標(biāo)記過程,它執(zhí)行全局引用處理和類卸載,以及回收完全空的分區(qū)和清空內(nèi)部數(shù)據(jù)結(jié)構(gòu).在remark和cleanup兩階段中間,G1會并發(fā)計(jì)算后續(xù)可回收的選定的老年代空間信息,此信息會最終在cleanup階段確定.
cleanup:這一次停頓會決定是否會有一個緊隨其后的空間回收階段,如果有空間回收階段發(fā)生,那么young-only階段會在最后為混合gc完成準(zhǔn)備.
空間回收階段:這一階段包含多個混合gc,除了回收年輕代,也會排泄老年代存活對象.這一階段會持續(xù)一段時間,直到G1發(fā)現(xiàn)排泄更多的老年代分區(qū)也不會得到與開銷等同價值的空閑空間的回報為止.
在空間回收階段之后,gc循環(huán)重啟,開始新一輪的young-only階段,如果應(yīng)用在收集對象存活信息過程中就已耗盡內(nèi)存,那么G1如其他垃圾收集器一樣,執(zhí)行備選的full gc.
G1停頓與cs,分代大小
G1在stop-the-world停頓中執(zhí)行空間回收,存活的對象會被從源區(qū)拷貝到目標(biāo)區(qū),存活對象的引用也會被相應(yīng)地調(diào)整到新地址.
對于非巨型對象,一個對象的目標(biāo)堆區(qū)決定于一些特定規(guī)則,若源對象是年輕代對象(eden區(qū)或幸存者區(qū)),則根據(jù)它們的年齡決定拷貝到幸存者區(qū)或老年代.
老年代對象拷貝到其他老年代.大對象則區(qū)別對待,G1只決定它們的存活與否,如果它們被判定為非存活對象,則就地回收,G1不會移動巨型對象.
受gc類型影響,cs可能包含不同種的分區(qū).在young only階段,cs只包含年輕代和可能被回收的潛在巨型對象區(qū);在空間回收階段,cs包含年輕代,可能被回收的潛在巨型對象區(qū),一些老年代候選區(qū).
G1會在并發(fā)周期中選定候選回收分區(qū),在remark停頓期間,G1選定那些較低占有率的,也就是包含大量空余空間的分區(qū),這些分區(qū)會接下來在remark和cleanup之間進(jìn)行后面的收集,cleanup停頓會根據(jù)回收它們的效率進(jìn)行排序,回收效率高的分區(qū),即看起來回收花費(fèi)時間少,包含更多空閑空間的會在后續(xù)的混合gc中優(yōu)先使用.
這一段話摘自官網(wǎng),官方說了"that contain more free space are preferred in subsequent mixed collections",可能會有不少看客看暈(包含作者自己),G1不是"垃圾優(yōu)先"嗎?它應(yīng)該優(yōu)先選取垃圾最多的分區(qū)去回收,作者在stackoverflow上找到了老外對此的解釋,看來有此疑問的不止作者一人.根據(jù)stackoverflow上的解釋,此處"包含更多的空閑空間"其實(shí)就是指包含更多的垃圾,這可能是英語漢語之間的一個美妙誤會吧.
從上圖可見,"mostly empty"是指包含盡可能多的"可回收的垃圾".貼子作者也指出,對于要回收的分區(qū),包含最大數(shù)量的可回收空間至少有兩點(diǎn)好處:一是可以盡快的獲得最多的空間,二是對于G1這種使用拷貝(標(biāo)記清除整理)的收集器,源分區(qū)存活對象越少,需要做的copy工作就越少,就可以越高效地回收最多的空間.
提到空間的回收以及cs的挑選,順便提一提比較簡單的堆空間大小調(diào)整問題.G1遵守調(diào)整java堆大小的標(biāo)準(zhǔn)規(guī)則,如-XX:InitialHeapSize可以指定java堆的最小大小,使用-XX:MaxHeapSize則指定了java堆的最大大小,-XX:MinHeapFreeRatio指定最小可用內(nèi)存的百分比,-XX:MaxHeapFreeRatio指定調(diào)整堆大小后最大空閑內(nèi)存占比.G1垃圾收集器僅會在remark和full gc的停頓期間調(diào)整堆的容量.這一過程會從操作系統(tǒng)獲取內(nèi)存或釋放內(nèi)存.
G1在每一次普通的young gc后都會為下一個增變器階段調(diào)整年輕代的大小.通過這種方式,G1可以依托于長期觀測實(shí)際的停頓時間來盡可能適配-XX:MaxGCPauseTimeMillis和-XX:PauseTimeIntervalMillis 指定的停頓時間.它會考慮相近大小的年輕代排泄會花費(fèi)多少時間.這會包括,多少對象會在回收過程中copy,以及這些對象彼此間是怎么聯(lián)系的.
如果未加其他約束,G1會通過在-XX:G1NewSizePercent和-XX:G1MaxNewSizePercent之間(或者用-XX:NewSize和-XX:MaxNewSize)靈活自適應(yīng)地調(diào)整年輕代大小的方式來盡可能達(dá)到用戶設(shè)定的停頓時間標(biāo)準(zhǔn).
空間回收階段也伴隨代大小調(diào)整.在此階段,G1試圖去最大化一次gc停頓中能回收的老年代空間量,此時會將年輕代設(shè)置為最小允許的大小,一般這個參數(shù)由-XX:G1NewSizePercent決定.在每一個混合gc開始時,G1會從候選cs中選出一組加入到cs中,這些附加的老年代分區(qū)包含三個部分:
第一部分是在排泄階段要保證的最小老年代分區(qū)集,這組老年代分區(qū)是由候選cs的分區(qū)數(shù)除以空間回收階段的長度決定.(空間回收階段長度由參數(shù)-XX:G1MixedGCCountTarget設(shè)置)如果G1預(yù)測在回收最小候選cs后還會有時間剩余,將會從候選cs添加其他老年代分區(qū),直到耗費(fèi)剩余時間的80%.
第三部分為一組可選的cs分區(qū),如果G1在本次停頓中遞增地完成了另外兩個部分的排泄后仍有時間剩余,則添加這組可選的分區(qū).前兩個分區(qū)集會在初始化收集過程中回收,可選cs中的分區(qū)則會在剩余停頓時間回收,通過這種方式實(shí)現(xiàn)了保證空間回收過程的同時提升了保證停頓時間和開銷最小的可能性.
當(dāng)候選cs中可回收的空間數(shù)量少于-XX:G1HeapWastePercent時,空間回收階段停止.
前面說過,最新幾版的JAVA對G1做出了不少改進(jìn),其中一個改進(jìn)就是周期gc,當(dāng)因應(yīng)用空閑而使得長久沒有g(shù)c時,虛擬機(jī)可能會持有大量的空閑內(nèi)存,而這些空閑內(nèi)存不能用在其他地方.為了避免這種浪費(fèi),G1可以強(qiáng)制規(guī)律性的gc,這需要提供-XX:G1PeriodicGCInterval選項(xiàng),它將決定G1考慮執(zhí)行一次gc的最小間隔時間,以毫秒為單位.如果從之前的gc停頓起過去了這些時間,且沒有任何過程中的并發(fā)周期,則G1會觸發(fā)額外的gc,這次觸發(fā)有不同的效果.
在young only階段,G1會根據(jù)是否指定-XX:-G1PeriodicGCInvokesConcurrent來決定使用哪一種停頓開始一個并發(fā)標(biāo)記停頓,如果未指定,則用"并發(fā)標(biāo)記停頓"來開始,否則使用full gc.在空間回收階段,G1會繼續(xù)觸發(fā)了適合當(dāng)前過過程的停頓類型的空間回收階段.
可使用選項(xiàng)-XX:G1PeriodicGCSystemLoadThreshold來細(xì)化是否觸發(fā)一個gc,如果JVM宿主機(jī)調(diào)用getloadavg()返回的值(一分鐘平均負(fù)載)超過了該值,則不會有周期gc運(yùn)行.
初始堆占用
初始堆占用比(IHOP)是一個閾值,它表示老年代占用的比例達(dá)到該值時觸發(fā)初始標(biāo)記(前面說過,G1中初始標(biāo)記同時于并發(fā)開始,并發(fā)開始包含普通gc和并發(fā)標(biāo)記).g1默認(rèn)自動地觀察標(biāo)記周期中老年代對象在一定時間分配的對象情況來斷定最優(yōu)的IHOP,也就是自適應(yīng)的IHOP.如果啟動了這個功能,并且當(dāng)前沒有足夠的觀測數(shù)據(jù)可決定一個良好的IHOP時,使用-XX:InitiatingHeapOccupancyPercent 設(shè)置的值作為IHOP.使用-XX:-G1UseAdaptiveIHOP可以關(guān)掉自適應(yīng)IHOP,則G1將永遠(yuǎn)使用-XX:InitiatingHeapOccupancyPercent作為默認(rèn)閾值.
當(dāng)沒有指定IHOP時,使用自適應(yīng)的IHOP將會嘗試為它準(zhǔn)備一個初值,這個初始老年代占用閾值默認(rèn)為當(dāng)前最大老年代空間減去-XX:G1HeapReservePercent(也被稱為extra buffer).
標(biāo)記
G1的標(biāo)記過程使用開始快照(SATB)算法.它會在初始化標(biāo)記停頓時提取當(dāng)前虛擬機(jī)堆快照,所有此時存活的對象和后續(xù)分配的對象都會被在標(biāo)記的剩余過程中被當(dāng)作存活(后者默認(rèn)標(biāo)記不需要追蹤).所以在空間回收階段,會對那些在標(biāo)記期間死亡的對象進(jìn)行冗余的工作(如果有異常發(fā)生),但是SATB算法減少了remark階段的停頓.好在這些被保守地當(dāng)作存活的死亡對象會在下一次標(biāo)記過程中識別.
堆緊張時行為
當(dāng)應(yīng)用持續(xù)向內(nèi)存中分配對象,導(dǎo)致沒有足夠的空間copy時,可能會導(dǎo)致排泄過程的失敗.排泄失敗意味著G1將試著去就地完成當(dāng)前的gc(已經(jīng)移動到新的位置或未來的及移動到新位置),此時將不會再移動未移動的對象,只會將對象間的一些引用關(guān)系進(jìn)行調(diào)整.排泄失敗可能會帶來一些額外開銷,但一般情況下應(yīng)當(dāng)和年輕代gc同一速度.在這一次gc排泄失敗之后,G1將會假定應(yīng)用如常,相當(dāng)于假定排泄失敗發(fā)生在gc的末尾,也就是大多對象已移動的情況,有足夠的空間保證應(yīng)用繼續(xù)運(yùn)行,能完成標(biāo)記和開始空間回收階段. 如果這個假定不能保持,那么G1只能進(jìn)行full gc,這將會進(jìn)行就地壓縮整理,會是一個非常緩慢的過程.
大對象行為
大對象是指大于或等于半個分區(qū)大小的對象.當(dāng)前分區(qū)大小可以用-XX:G1HeapRegionSize 選項(xiàng)來設(shè)置.
這些大對象有時會被特殊對待,每個大對象會在老年代分區(qū)中連續(xù)分配.對象的開始點(diǎn)總是在該分區(qū)組中的第一個分區(qū)的起始,最后一個分區(qū)的剩余部分將不會在對象分配中使用,直到整個對象回收為止.
一般情況下,大對象只可以在標(biāo)記過程的末尾的cleanup停頓期間進(jìn)行回收,或者在它不可達(dá)后的full gc中回收.但對于一些特殊的大型對象,如所有元素均為基本類型,G1會在任何gc停頓過程中嘗試碰運(yùn)氣回收它們.這個特性默認(rèn)開啟,可以使用選項(xiàng)-XX:G1EagerReclaimHumongousObjects 關(guān)閉.
大對象的分配可能會導(dǎo)致gc停頓過早地發(fā)生.G1會在任何一個大對象分配時檢查初始堆占用比(IHOP),這可能會強(qiáng)制立即開始young gc的初始標(biāo)記.大對象即使在full gc中也從不移動,也可能會導(dǎo)致full gc過程緩慢或者雖然存在大量空閑空間卻因大量的內(nèi)存碎片而出人意料的oom.
對比其他gc
與其他gc對比,簡單列舉區(qū)別如下:
Parallel gc也會整理和回收老年代空間,但只能作為一個整體進(jìn)行.G1相當(dāng)于遞進(jìn)地,增量地用多個更短的gc過程完成了同樣的工作.這樣減少了停頓時間,也消費(fèi)了一些吞吐量.
與cms相似,G1并發(fā)完成老年代的空間回收,然而cms不能解決老年代堆的碎片化問題,最終只能導(dǎo)致長運(yùn)行的full gc.
G1可能比上述垃圾收集器開銷更大,因?yàn)樗牟l(fā)特性而影響到了吞吐量.
ZGC的目標(biāo)在于超大堆,追求更短的停頓時間,卻更大的消費(fèi)了吞吐量.
取決于G1的工作方式,它有一些獨(dú)有的機(jī)制來提升gc效率.如G1可以在任何collection過程中回收完全空的,大的老年代分區(qū),這可以避免很多其他不必要的gc,不太費(fèi)力的釋放大量空間.G1也可以嘗試并發(fā)地將堆內(nèi)存中的重復(fù)字符串去重.
回收老年代中空的,巨型對象默認(rèn)開啟,可使用選項(xiàng)-XX:-G1EagerReclaimHumongousObjects開啟,字符串去重默認(rèn)關(guān)閉,可使用-XX:+G1EnableStringDeduplication開啟.
書寫心得
到此G1垃圾收集器簡述就寫完了,主要參考資料為官方的幾篇文章和文檔,未涉及復(fù)雜高深的內(nèi)部實(shí)現(xiàn),最近幾個版本的java都在不停地優(yōu)化,當(dāng)然gc只是其中一小部分,大到即時編譯和gc,小到線程握手和引入nests等,感覺java越來越重視"內(nèi)功"了. 近幾版除了G1進(jìn)行了改良以外,官方也推出了幾種新的垃圾收集器: Epsilon GC是jdk11推出的無操作(no-op)垃圾收集器,方便于我們多帶帶觀測一個應(yīng)用的內(nèi)存分配情況,不受回收干擾. zgc是一個旨在超大堆下保持低延遲的回收器,據(jù)官方wiki,在jdk13中已經(jīng)將支持的最大堆從4T提升到了16T,回收時間不受堆內(nèi)存大小的影響,相應(yīng)的著色指針技術(shù),讀屏障技術(shù)簡直讓人嘆為觀止,它在jdk11中開放體驗(yàn)版,但不支持類卸載,jdk12中補(bǔ)上了這一功能. Shenandoah 也是一個超大堆低延遲的回收器,jdk12中開放,不同于zgc的是它使用了"間接指針"的技術(shù)實(shí)現(xiàn)對象拷貝過程的并發(fā)進(jìn)行. 總之,發(fā)自內(nèi)心地佩服這些作者,我輩學(xué)習(xí)之楷模.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74735.html
摘要:本文是個人在企業(yè)內(nèi)部分享使用的簡要大綱,列舉了的重要更新,文章的結(jié)構(gòu)較簡單,也不規(guī)范,鑒于近期寫若干文章時總會忘記一些新特性所處的版本,特將此大綱流留用。 本文是個人在企業(yè)內(nèi)部分享使用的簡要大綱,列舉了JAVA9-12的重要更新,文章的結(jié)構(gòu)較簡單,也不規(guī)范,鑒于近期寫若干文章時總會忘記一些新特性所處的版本,特將此大綱流copy留用。 一 JAVA9 新特性 1.Java Platfo...
摘要:之前根據(jù)的內(nèi)存管理白皮書介紹了在分代算法中的幾個垃圾收集器,本文將介紹垃圾收集器。本節(jié)介紹的收集過程,收集器主要包括了以下種操作年輕代收集并發(fā)收集,和應(yīng)用線程同時執(zhí)行混合式垃圾收集必要時的接下來,我們進(jìn)行一一介紹。 之前根據(jù) Sun 的內(nèi)存管理白皮書介紹了在 HotSpot JVM 分代算法中的幾個垃圾收集器,本文將介紹 G1 垃圾收集器。 G1 的主要關(guān)注點(diǎn)在于達(dá)到可控的停頓時間,在...
摘要:表示允許垃圾收集線程處理本次垃圾收集開始前沒有處理好的日志緩沖區(qū),這可以確保當(dāng)前分區(qū)的是最新的。垃圾收集線程在完成其他任務(wù)的時間展示每個垃圾收集線程的最小最大平均差值和總共時間。 本文翻譯自:https://www.redhat.com/en/blog/collecting-and-reading-g1-garbage-collector-logs-part-2?source=auth...
摘要:適用收集場景新生代收集老年代收集并行收集器又叫吞吐量收集器應(yīng)用于多核系統(tǒng)。它是為了平衡延時和吞吐量之間的一種最優(yōu)關(guān)系。 回顧傳統(tǒng)垃圾回收器 HotSpot 垃圾收集器實(shí)現(xiàn) Serial Collector(串型收集器) 使用場景,大多數(shù)服務(wù)器是單核CPU。適用收集場景:1. 新生代收集(Young Generation Collection)2. 老年代收集(Old Genera...
閱讀 2336·2021-10-08 10:04
閱讀 1117·2021-09-03 10:40
閱讀 1163·2019-08-30 15:53
閱讀 3321·2019-08-30 13:13
閱讀 2939·2019-08-30 12:55
閱讀 2292·2019-08-29 13:21
閱讀 1375·2019-08-26 12:12
閱讀 2765·2019-08-26 10:37