G1 垃圾回收器
一、垃圾收集器簡(jiǎn)介
1、發(fā)展歷程
-
第一階段,Serial(串行)收集器
在jdk1.3.1之前,java虛擬機(jī)僅僅能使用Serial收集器。 Serial收集器是一個(gè)單線(xiàn)程的收集器,但它的“單線(xiàn)程”的意義并不僅僅是說(shuō)明它只會(huì)使用一個(gè)CPU或一條收集線(xiàn)程去完成垃圾收集工作,更重要的是在它進(jìn)行垃圾收集時(shí),必須暫停其他所有的工作線(xiàn)程,直到它收集結(jié)束。 -
第二階段,Parallel(并行)收集器
Parallel收集器也稱(chēng)吞吐量收集器,相比Serial收集器,Parallel最主要的優(yōu)勢(shì)在于使用多線(xiàn)程去完成垃圾清理工作,這樣可以充分利用多核的特性,大幅降低gc時(shí)間。 -
第三階段,CMS(并發(fā))收集器
CMS收集器在Minor GC時(shí)會(huì)暫停所有的應(yīng)用線(xiàn)程,并以多線(xiàn)程的方式進(jìn)行垃圾回收。在Full GC時(shí)不再暫停應(yīng)用線(xiàn)程,而是使用若干個(gè)后臺(tái)線(xiàn)程定期的對(duì)老年代空間進(jìn)行掃描,及時(shí)回收其中不再使用的對(duì)象。 -
第四階段,G1(并發(fā))收集器
G1收集器(或者垃圾優(yōu)先收集器)的設(shè)計(jì)初衷是為了盡量縮短處理超大堆(大于4GB)時(shí)產(chǎn)生的停頓。相對(duì)于CMS的優(yōu)勢(shì)而言是內(nèi)存碎片的產(chǎn)生率大大降低。
2、種類(lèi)
-
新生代
- Serial (第一代)
- PraNew (第二代)
- Parallel Scavenge (第三代)
- G1收集器(第四代)
-
老年代
- Serial Old (第一代)
- Parallel Old (第二代)
- CMS (第三代)
- G1
二、G1 介紹
1、概述
- G1最大的特點(diǎn)是引入分區(qū)的思路,弱化了分代的概念
- 合理利用垃圾收集各個(gè)周期的資源,解決了其他收集器甚至CMS的眾多缺陷
- 算法:基于標(biāo)記-整理算法,不會(huì)產(chǎn)生空間碎片,分配大對(duì)象時(shí)不會(huì)因得不到連續(xù)空間而提前觸發(fā) FULL GC
- 停頓時(shí)間可控: G1可以通過(guò)設(shè)置預(yù)期停頓時(shí)間 Pause Time 來(lái)控制垃圾收集時(shí)間避免應(yīng)用雪崩現(xiàn)象
- 并行與并發(fā):G1 能充分利用 多核 CPU 的硬件優(yōu)勢(shì)來(lái)縮短 stop the world 的停頓時(shí)間
- CMS 的堆分為 PermGen、YoungGen、OldGen;而YoungGen又分了兩個(gè)survivo區(qū)域
G1 的堆被分為區(qū)域(region),每個(gè)區(qū)域雖然保留了新老代概念,但收集器是以整個(gè)區(qū)域?yàn)閱挝皇占?/li> - G1在回收內(nèi)存后會(huì)馬上同時(shí)做合并空閑內(nèi)存的工作、而 CMS 默認(rèn)是在STW(stop the world)時(shí)做
- G1會(huì)在 Young GC 中使用、而 CMS 只能在 O 區(qū)使用
- G1垃圾收集算法主要應(yīng)用在多CPU大內(nèi)存的服務(wù)中,在滿(mǎn)足高吞吐量的同時(shí),盡可能的滿(mǎn)足垃圾回收時(shí)的暫停時(shí)間。
- 就目前而言、CMS還是默認(rèn)首選的GC策略、可能在以下場(chǎng)景下G1更適合:
- 服務(wù)端多核CPU、JVM內(nèi)存占用較大的應(yīng)用(至少大于4G)
- 應(yīng)用在運(yùn)行過(guò)程中會(huì)產(chǎn)生大量?jī)?nèi)存碎片、需要經(jīng)常壓縮空間
- 想要更可控、可預(yù)期的GC停頓周期,防止高并發(fā)下應(yīng)用雪崩現(xiàn)象
2、G1的堆內(nèi)存算法
G1之前的JVM內(nèi)存模型:
G1收集器的內(nèi)存模型:
-
G1 收集器將整個(gè)Java 堆劃分成約 2048 個(gè)大小相同的獨(dú)立 Region 塊,每個(gè)Region塊大小根據(jù)堆空間的實(shí)際大小而定
可以通過(guò) -XX :G1HeapRegionSize 設(shè)定,值在 1MB~32MB 之間且為 2 的次冪
-
region 可能屬于 Eden、Survivor、0ld、Humongous 區(qū)域,但一個(gè)region只可能屬于一個(gè)角色
-
Humongous 作用:用來(lái)專(zhuān)門(mén)存放大對(duì)象,一般使用連續(xù) region 區(qū)存儲(chǔ),G1的大多數(shù)行為都把H區(qū)
作為老年代的一部分來(lái)看待
3、G1 的特點(diǎn)、缺點(diǎn)
3.1 特點(diǎn)
①. 并行和并發(fā)
- 并行性:G1 在回收期間,可以有多個(gè)GC線(xiàn)程同時(shí)工作,有效利用多核計(jì)算能力
- 并發(fā)性:G1擁有與應(yīng)用程序交替執(zhí)行的能力,部分工作可以和應(yīng)用程序同時(shí)執(zhí)行
②. 分代收集
- 從分代上看,G1 仍屬于分代型垃圾回收器,會(huì)區(qū)分年輕代和老年代,年輕代依然有 Eden 區(qū)和 Survivor 區(qū)
但從堆的結(jié)構(gòu)上看,不要求整個(gè) Eden 區(qū)、年輕代或老年代都連續(xù),也不再堅(jiān)持固定大小和固定數(shù)量 - 將堆空間分為若干個(gè)區(qū)域(Region),這些區(qū)域中包含了邏輯上的年輕代和老年代
- 和之前的各類(lèi)回收器不同,其同時(shí)兼顧年輕代和老年代
③. 空間整合
- 內(nèi)存回收以 region 為基本單位,Region之間是復(fù)制算法,但整體是標(biāo)記-壓縮算法,可以避免內(nèi)存碎片,有利于程序長(zhǎng)時(shí)間運(yùn)行,分配大對(duì)象時(shí)不會(huì)因?yàn)闊o(wú)法找到連續(xù)內(nèi)存空間而提前觸發(fā)下一次GC
④. 可預(yù)測(cè)的停頓時(shí)間模型(即:軟實(shí)時(shí)soft real一time)
可預(yù)測(cè)的停頓時(shí)間模型:能讓使用者明確指定在一個(gè)長(zhǎng)度為 M 毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不得超過(guò) N 毫秒,通過(guò)參數(shù) -XX:MaxGCPauseMillis 設(shè)置
- 由于分區(qū)的原因,G1可以只選取部分區(qū)域進(jìn)行內(nèi)存回收,縮小回收范圍,避免全局停頓情況的發(fā)生
- G1 跟蹤各個(gè) Region 中垃圾堆積的價(jià)值大小(回收所獲得的空間大小以及回收所需時(shí)間的經(jīng)驗(yàn)值),在后臺(tái)維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間,優(yōu)先回收價(jià)值最大的 Region,保證了G1收集器在有限的時(shí)間內(nèi)可以獲取盡可能高的收集效率。
- 相比于 CMS GC,G1未必能做到CMS在最好情況下的延時(shí)停頓,但最差情況要好很多
3.2 缺點(diǎn)
- 相較于CMS,G1 還不具備全方位、壓倒性?xún)?yōu)勢(shì),如:G1 無(wú)論是為了垃圾收集產(chǎn)生的內(nèi)存占用,還是程序運(yùn)行時(shí)的額外執(zhí)行負(fù)載都要比 CMS 高
- 小內(nèi)存應(yīng)用上,CMS 的表現(xiàn)大概率會(huì)優(yōu)于 G1,而 G1 在大內(nèi)存應(yīng)用上則發(fā)揮其優(yōu)勢(shì),平衡點(diǎn)在6-8GB之間
4、G1 獨(dú)有概念
4.1 RSet 與 card
RSet 與 card 專(zhuān)門(mén)用來(lái)處理 Old 區(qū)到 Young 區(qū)的引用
Young 區(qū)到 Old 區(qū)的引用不需要單獨(dú)處理,因?yàn)?Young 區(qū)中的對(duì)象本身變化比較大,沒(méi)必要浪費(fèi)空間去記錄下來(lái)
- RSet:用來(lái)記錄外部指向本 Region 的所有引用,每個(gè) Region 維護(hù)一個(gè) RSet
- Card: JVM 將內(nèi)存劃分成了固定大小的 Card,類(lèi)比物理內(nèi)存上 page 的概念
- 每個(gè) Region 分成多個(gè) Card,其中綠色部分的 Card 表示該 Card 中有對(duì)象引用了其他 Card 中的對(duì)象,這種引用關(guān)系用藍(lán)色實(shí)線(xiàn)表示。
- RSet 其實(shí)是一個(gè)HashTable,Key 是 Region 的起始地址,Value 是Card Table(字節(jié)數(shù)組),字節(jié)數(shù)組下標(biāo)表示 Card 的空間地址,當(dāng)該地址空間被引用時(shí)會(huì)被標(biāo)記為 dirty_card
RSet 的更新:
G1 采用 post-write barrier 和 concurrent refinement threads 更新 RSet,減少每次給引用類(lèi)型的字段賦值都要更新 RSet帶來(lái)的開(kāi)銷(xiāo)
java 層面給 old 對(duì)象的 p 字段賦值 young 對(duì)象之后,jvm 底層會(huì)執(zhí)行 oop_store 方法
在賦值動(dòng)作的前后,JVM插入一個(gè) pre-write barrier 和 post-write barrier
post-write barrier 的最終動(dòng)作如下:
- 找到該字段所在的位置(Card),并設(shè)置為 dirty_card
- 若當(dāng)前是應(yīng)用線(xiàn)程,每個(gè)Java線(xiàn)程有一個(gè) dirty card queue,把該 card 插入隊(duì)列
- 除了每個(gè)線(xiàn)程自帶的 dirty card queue,還有一個(gè)全局共享的 queue
RSet 更新操作交由多個(gè) ConcurrentG1RefineThread 并發(fā)完成,每當(dāng)全局隊(duì)列集合超過(guò)一定閾值后,ConcurrentG1RefineThread 會(huì)取出若干個(gè)隊(duì)列,遍歷每個(gè)隊(duì)列中記錄的card,并進(jìn)行處理,大概實(shí)現(xiàn)邏輯如下:
1、根據(jù) card 的地址,計(jì)算出 card 所在的 Region
2、若 Region 不存在,或 Region 是 Young 區(qū),或該 Region 在回收集合中,則不進(jìn)行處理
3、最終使用閉合函數(shù) G1UpdateRSOrPushRefOopClosure::do_oop_nv() 的處理該 card 中的對(duì)象
refinement threads 線(xiàn)程數(shù)量可以通過(guò) -XX:G1ConcRefinementThreads 或 -XX:ParallelGCThreads 參數(shù)設(shè)置
4.2 Collection Set
收集集合(CSet):一組可被回收的分區(qū)集合。在CSet中存活的數(shù)據(jù)會(huì)在GC過(guò)程中被移動(dòng)到另一個(gè)可用分區(qū),CSet中的分區(qū)可以來(lái)自eden空間、survivor空間、老年代
GC時(shí)在CSet中的所有存活數(shù)據(jù)都會(huì)被轉(zhuǎn)移,分區(qū)釋放回空閑分區(qū)隊(duì)列
4.3 PLAB
在 GC 線(xiàn)程的晉升本地分配緩沖區(qū)(PLAB)中,對(duì)象晉升到 survivor 分區(qū)或老年代分區(qū)
每個(gè)線(xiàn)程有獨(dú)立的PLAB,作用是避免多線(xiàn)程競(jìng)爭(zhēng)相同數(shù)據(jù)
4.4 TLAB
JVM使用線(xiàn)程本地分配緩存TLAB 這種線(xiàn)程專(zhuān)屬的區(qū)間,來(lái)避免多線(xiàn)程沖突(無(wú)鎖方式),提高對(duì)象分配效率
TLAB 本身占用了 Eden 空間,即 JVM 會(huì)為每一個(gè)線(xiàn)程都分配一塊 TLAB 空間
5、G1 回收過(guò)程
大致流程:
年輕代回收時(shí),G1 暫停所有應(yīng)用程序線(xiàn)程,啟動(dòng)多線(xiàn)程執(zhí)行年輕代回收,然后移動(dòng)存活對(duì)象到Survivor 或 O 區(qū)
一次只需要掃描/回收一小部分老年代的 Region 就可以
5.1 Young GC
- 回收時(shí)機(jī):當(dāng) Eden 空間耗盡時(shí),G1會(huì)啟動(dòng)一次年輕代垃圾回收過(guò)程
- 回收對(duì)象:年輕代垃圾回收只會(huì)回收 Eden 區(qū)和 Survivor 區(qū)
回收流程:
- 第一階段,根掃描,根引用連同 RSet 記錄的外部引用作為掃描存活對(duì)象的入口
- 根是指 static 變量指向的對(duì)象,正在執(zhí)行的方法調(diào)用鏈條上的局部變量等
- 掃描 remembered Set,看是否有老年代中的對(duì)象引用了新生代對(duì)象
- 第二階段,更新 RSet
- 處理 dirty card queue 中的 card,更新 RSet 后,可以準(zhǔn)確反映老年代對(duì)所在的內(nèi)存分段中對(duì)象的引用
- 第三階段,處理 RSet
- 識(shí)別被老年代對(duì)象指向的 Eden 中的對(duì)象,這些被指向的 Eden 中的對(duì)象被認(rèn)為是存活的對(duì)象
- 第四階段,復(fù)制對(duì)象,遍歷對(duì)象樹(shù)
- Eden 區(qū)內(nèi)存段中存活的對(duì)象會(huì)被復(fù)制到 Survivor 區(qū)中空的內(nèi)存分段
- Survivor 區(qū)內(nèi)存段中存活的對(duì)象如果年齡未達(dá)閾值,年齡會(huì)加1,達(dá)到閥值會(huì)被復(fù)制到 old 區(qū)中空的內(nèi)存分段
- 若 Survivor 空間不夠,Eden 空間的部分?jǐn)?shù)據(jù)會(huì)直接晉升到老年代空間
- 第五階段,處理引用,處理 Soft、Weak、Phantom、Final、JNI Weak 等引用
- 最終 Eden 空間的數(shù)據(jù)為空,GC停止工作,而目標(biāo)內(nèi)存中的對(duì)象都是連續(xù)存儲(chǔ)的,沒(méi)有碎片,所以復(fù)制過(guò)程可以達(dá)到內(nèi)存整理的效果,減少碎片
5.2 Concurrent Marking
初始標(biāo)記階段:標(biāo)記從根節(jié)點(diǎn)直接可達(dá)的對(duì)象,這個(gè)階段是 STW 的,并且會(huì)觸發(fā)一次年輕代 GC
根區(qū)域掃描(Root Region Scanning):G1 GC 掃描 Survivor 區(qū)直接可達(dá)的老年代區(qū)域?qū)ο?/strong>,并標(biāo)記被引用的對(duì)象
這一過(guò)程必須在 young GC 之前完成(YoungGC時(shí),會(huì)動(dòng) Survivor 區(qū),所以這一過(guò)程必須在young GC之前完成)
并發(fā)標(biāo)記(Concurrent Marking):在并發(fā)標(biāo)記階段,若發(fā)現(xiàn)區(qū)域?qū)ο笾械乃袑?duì)象都是垃圾,則這個(gè)區(qū)域會(huì)被立即回收,此過(guò)程可能被 young GC 中斷
并發(fā)標(biāo)記過(guò)程中,會(huì)計(jì)算每個(gè)區(qū)域的對(duì)象活性(區(qū)域中存活對(duì)象的比例)
再次標(biāo)記(Remark):修正上一次的標(biāo)記結(jié)果,是 STW
獨(dú)占清理(cleanup,STW):計(jì)算各個(gè)區(qū)域的存活對(duì)象和 GC 回收比例,并進(jìn)行排序,識(shí)別可以混合回收的區(qū)域,是STW 的
這個(gè)階段并不會(huì)實(shí)際上去做垃圾的收集
并發(fā)清理階段:識(shí)別并清理完全空閑的區(qū)域
5.3 Mixed GC
- 觸發(fā)時(shí)機(jī):老年代的堆占有率達(dá)到參數(shù) -XX:InitiatingHeapOccupancyPercent 設(shè)定的值則觸發(fā)
- 回收對(duì)象:回收所有的 Young 和部分 Old(根據(jù)期望的GC停頓時(shí)間確定old區(qū)垃圾收集的優(yōu)先順序)以及大對(duì)象區(qū)
- 回收過(guò)程:MixedGC 主要使用復(fù)制算法,把各個(gè) region 中存活的對(duì)象拷貝到別的 region 里去,拷貝過(guò)程中若發(fā)現(xiàn)沒(méi)有足夠的空 region 能夠承載拷貝對(duì)象就會(huì)觸發(fā)一次 Full GC
- 并發(fā)標(biāo)記結(jié)束以后,老年代中百分百為垃圾的內(nèi)存分段被回收了,部分為垃圾的內(nèi)存分段被計(jì)算了出來(lái)。默認(rèn)情況下,這些老年代的內(nèi)存分段會(huì)分8次(可以通過(guò)-XX:G1MixedGCCountTarget設(shè)置)被回收
- 混合回收的回收集(Collection Set)包括八分之一的老年代內(nèi)存分段,Eden區(qū) 內(nèi)存分段,Survivor區(qū)內(nèi)存分段。 混合回收的算法和年輕代回收的算法完全一樣,只是回收集多了老年代的內(nèi)存分段。具體過(guò)程請(qǐng)參考上面的年輕代回收過(guò)程。
- 由于老年代中的內(nèi)存分段默認(rèn)分8次回收,G1會(huì)優(yōu)先回收垃圾多的內(nèi)存分段。垃圾占內(nèi)存分段比例越高的,越會(huì)被先回收。并且有一個(gè)閾值會(huì)決定內(nèi)存分段是否被回收,-XX:G1MixedGCLiveThresholdPercent,默認(rèn)為65%,意思是垃圾占內(nèi)存分段比例要達(dá)到65%才會(huì)被回收。如果垃圾占比太低,意味著存活的對(duì)象占比高,在復(fù)制的時(shí)候會(huì)花費(fèi)更多的時(shí)間
- 混合回收并不一定 要進(jìn)行8次。有一個(gè)閾值**-XX :G1HeapWastePercent**,默認(rèn)值為10%,意思是允許整個(gè)堆內(nèi)存中有10%的空間被浪費(fèi),意味著如果發(fā)現(xiàn)可以回收的垃圾占堆內(nèi)存的比例低于10%,則不再進(jìn)行混合回收。因?yàn)镚C會(huì)花費(fèi)很多的時(shí)間但是回收到的內(nèi)存卻很少
5.4 Full GC
- 特點(diǎn):G1會(huì)停止應(yīng)用程序的執(zhí)行(Stop-The-World) ,使用單線(xiàn)程的內(nèi)存回收算法進(jìn)行垃圾回收,性能會(huì)非常差,應(yīng)用程序停頓時(shí)間會(huì)很長(zhǎng)。
- 導(dǎo)致 G1 Full GC 的原因可能有兩個(gè): .
- 回收時(shí),沒(méi)有足夠的 to-space 來(lái)存放晉升對(duì)象
- 并發(fā)處理過(guò)程沒(méi)完成空間就耗盡
總結(jié)
- 上一篇: 人工智能机器视觉专业英语积累
- 下一篇: 犹如“笼中困兽”的中国半导体,正在冒着敌