在前一篇的文章《HotSpot垃圾回收算法概述》里面仰税,對于Serial, Parallel和CMS幾種垃圾回收器做了比較詳細(xì)的描述硫眯。但是對于G1的敘述是比較粗糙的疲陕。這篇文章則是提供了G1垃圾回收器的詳細(xì)分析补君。
概述
G1垃圾回收器是在Java7 update 4之后引入的一個新的垃圾回收器。G1是一個分代的芭毙,增量的筋蓖,并行與并發(fā)的標(biāo)記-復(fù)制垃圾回收器。它的設(shè)計(jì)目標(biāo)是為了適應(yīng)現(xiàn)在不斷擴(kuò)大的內(nèi)存和不斷增加的處理器數(shù)量稿蹲,進(jìn)一步降低暫停時間(pause time)扭勉,同時兼顧良好的吞吐量。G1回收器和CMS比起來苛聘,有以下不同:
- G1垃圾回收器是compacting的涂炎,因此其回收得到的空間是連續(xù)的。這避免了CMS回收器因?yàn)椴贿B續(xù)空間所造成的問題设哗。如需要更大的堆空間唱捣,更多的floating garbage。連續(xù)空間意味著G1垃圾回收器可以不必采用空閑鏈表的內(nèi)存分配方式网梢,而可以直接采用bump-the-pointer的方式震缭;
- G1回收器的內(nèi)存與CMS回收器要求的內(nèi)存模型有極大的不同。G1將內(nèi)存劃分一個個固定大小的region战虏,每個region可以是年輕代拣宰、老年代的一個。內(nèi)存的回收是以region作為基本單位的烦感;
G1還有一個及其重要的特性:軟實(shí)時(soft real-time)巡社。所謂的實(shí)時垃圾回收,是指在要求的時間內(nèi)完成垃圾回收手趣∩胃茫“軟實(shí)時”則是指,用戶可以指定垃圾回收時間的限時绿渣,G1會努力在這個時限內(nèi)完成垃圾回收朝群,但是G1并不擔(dān)保每次都能在這個時限內(nèi)完成垃圾回收。通過設(shè)定一個合理的目標(biāo)中符,可以讓達(dá)到90%以上的垃圾回收時間都在這個時限內(nèi)姜胖。
數(shù)據(jù)結(jié)構(gòu)
在了解G1垃圾回收器的算法前,先熟悉在G1中使用的一些數(shù)據(jù)結(jié)構(gòu)和概念是有用的淀散。如果只是想大概了解一下G1垃圾回收器谭期,那么此節(jié)可以跳過。即便想詳細(xì)了解G1回收器的細(xì)節(jié)也可以先跳過這一節(jié)吧凉,直接閱讀下面的算法詳解隧出,遇到了不明白的數(shù)據(jù)結(jié)構(gòu)和概念,再回來此處尋找解釋阀捅。
G1垃圾回收器的復(fù)雜難懂胀瞪,有很大一部分原因是因?yàn)檫@些數(shù)據(jù)結(jié)構(gòu)。
Heap Region
本質(zhì)上來說,G1垃圾回收器依然是一個分代垃圾回收器凄诞。但是它與一般的回收器所不同的是圆雁,它引入了額外的概念,Region帆谍。G1垃圾回收器把堆劃分成一個個大小相同的Region伪朽。在HotSpot的實(shí)現(xiàn)中,整個堆被劃分成2048左右個Region汛蝙。每個Region的大小在1-32MB之間烈涮,具體多大取決于堆的大小。
G1垃圾回收器的分代也是建立在這些Region的基礎(chǔ)上的窖剑。對于Region來說坚洽,它會有一個分代的類型,并且是唯一一個西土。即讶舰,每一個Region,它要么是young的需了,要么是old的跳昼。還有一類十分特殊的Humongous。所謂的Humongous肋乍,就是一個對象的大小超過了某一個閾值——HotSpot中是Region的1/2鹅颊,那么它會被標(biāo)記為Humongous。如果我們審視HotSpot的其余的垃圾回收器住拭,可以發(fā)現(xiàn)這種對象以前被稱為大對象挪略,會被直接分配老年代历帚。而在G1回收器中滔岳,則是做了特殊的處理。
G1并不要求相同類型的region要相鄰挽牢。換言之谱煤,就是G1回收器不要求它們連續(xù)。當(dāng)然在邏輯上禽拔,分代依舊是連續(xù)的刘离。因此,一種典型的分配可能是:
注:圖片來自G1: One Garbage Collector To Rule Them All
其中E代表的是Eden睹栖,S代表的是Survivor硫惕,H代表的是Humongous,剩余的深藍(lán)色代表的是Old(或者Tenured)野来,灰色的代表的是空閑的region恼除。
每一個分配的Region,都可以分成兩個部分,已分配的和未被分配的豁辉。它們之間的界限被稱為top令野。總體上來說徽级,把一個對象分配到Region內(nèi)气破,只需要簡單增加top的值。這個做法實(shí)際上就是bump-the-pointer餐抢。過程如下:
Region可以說是G1回收器一次回收的最小單元现使。即每一次回收都是回收N個Region。這個N是多少弹澎,主要受到G1回收的效率和用戶設(shè)置的軟實(shí)時目標(biāo)有關(guān)朴下。每一次的回收,G1會選擇可能回收最多垃圾的Region進(jìn)行回收苦蒿。與此同時殴胧,G1回收器會維護(hù)一個空間Region的鏈表。每次回收之后的Region都會被加入到這個鏈表中佩迟。
每一次都只有一個Region處于被分配的狀態(tài)中团滥,被稱為current region。在多線程的情況下报强,這會帶來并發(fā)的問題灸姊。G1回收器采用和CMS一樣的TLABs的手段。即為每一個線程分配一個Buffer秉溉,線程分配內(nèi)存就在這個Buffer內(nèi)分配力惯。但是當(dāng)線程耗盡了自己的Buffer之后,需要申請新的Buffer召嘶。這個時候依然會帶來并發(fā)的問題父晶。G1回收器采用的是CAS(Compate And Swap)操作。
為線程分配Buffer的過程大概是:
- 記錄top值弄跌;
- 準(zhǔn)備分配甲喝;
- 比較記錄的top值和現(xiàn)在的top值,如果一樣铛只,則執(zhí)行分配埠胖,并且更新top的值;否則淳玩,重復(fù)1直撤;
顯然的,采用TLABs的技術(shù)蜕着,就會帶來碎片谋竖。舉例來說,當(dāng)一個線程在自己的Buffer里面分配的時候,雖然Buffer里面還有剩余的空間圈盔,但是卻因?yàn)榉峙涞膶ο筮^大以至于這些空閑空間無法容納豹芯,此時線程只能去申請新的Buffer,而原來的Buffer中的空閑空間就被浪費(fèi)了驱敲。Buffer的大小和線程數(shù)量都會影響這些碎片的多寡铁蹈。
Remember Set和Card Table
RS(Remember Set)是一種抽象概念,用于記錄從非收集部分指向收集部分的指針的集合众眨。
在傳統(tǒng)的分代垃圾回收算法里面握牧,RS(Remember Set)被用來記錄分代之間的指針。在G1回收器里面娩梨,RS被用來記錄從其他Region指向一個Region的指針情況沿腰。因此,一個Region就會有一個RS狈定。這種記錄可以帶來一個極大的好處:在回收一個Region的時候不需要執(zhí)行全堆掃描颂龙,只需要檢查它的RS就可以找到外部引用,而這些引用就是initial mark的根之一纽什。
那么措嵌,如果一個線程修改了Region內(nèi)部的引用,就必須要去通知RS芦缰,更改其中的記錄企巢。為了達(dá)到這種目的,G1回收器引入了一種新的結(jié)構(gòu)让蕾,CT(Card Table)——卡表浪规。每一個Region,又被分成了固定大小的若干張卡(Card)探孝。每一張卡笋婿,都用一個Byte來記錄是否修改過≡俟茫卡表即這些byte的集合萌抵。實(shí)際上找御,如果把RS理解成一個概念模型元镀,那么CT就可以說是RS的一種實(shí)現(xiàn)方式。
從第一感覺霎桅,或者出于直覺的考慮栖疑,使用一個bit來記錄一張卡是否被修改過,就已經(jīng)足夠了滔驶。而使用一個byte會造成更多的空間開銷遇革。但是實(shí)際上,使用一個byte來記錄一張卡是否被修改過,會比使用一個bit來記錄效率更高萝快。更多細(xì)節(jié)參閱資料3锻霎。
在RS的修改上也會遇到并發(fā)的問題。因?yàn)橐粋€Region可能有多個線程在并發(fā)修改揪漩,因此它們也會并發(fā)修改RS旋恼。為了避免這樣一種沖突,G1垃圾回收器進(jìn)一步把RS劃分成了多個哈希表奄容。每一個線程都在各自的哈希表里面修改冰更。最終,從邏輯上來說昂勒,RS就是這些哈希表的集合蜀细。哈希表是實(shí)現(xiàn)RS的一種通常的方式之一。它有一個極大的好處就是能夠去除重復(fù)戈盈。這意味著奠衔,RS的大小將和修改的指針數(shù)量相當(dāng)。而在不去重的情況下塘娶,RS的數(shù)量和寫操作的數(shù)量相當(dāng)涣觉。
整個關(guān)系如下:
圖中RS的虛線表名的是,RS并不是一個和Card Table獨(dú)立的血柳,不同的數(shù)據(jù)結(jié)構(gòu)官册,而是指RS是一個概念模型。實(shí)際上难捌,Card Table是RS的一種實(shí)現(xiàn)方式膝宁。
Remember Set的寫屏障
寫屏障是指,在改變特定內(nèi)存的值(實(shí)際上也就是寫入內(nèi)存)的時候額外執(zhí)行的一些動作根吁。在大多數(shù)的垃圾回收算法中员淫,都利用到了寫屏障。寫屏障通常用于在運(yùn)行時探測并記錄回收相關(guān)指針(interesting pointer)击敌,在回收器只回收堆中部分區(qū)域的時候介返,任何來自該區(qū)域外的指針都需要被寫屏障捕獲,這些指針將會在垃圾回收的時候作為標(biāo)記開始的根沃斤。JAVA使用的其余的分代的垃圾回收器圣蝎,都有寫屏障。舉例來說衡瓶,每一次將一個老年代對象的引用修改為指向年輕代對象徘公,都會被寫屏障捕獲,并且記錄下來哮针。因此在年輕代回收的時候关面,就可以避免掃描整個老年代來查找根坦袍。
G1垃圾回收器的寫屏障和RS是相輔相成的,也就是記錄Region內(nèi)部的指針等太。這種記錄發(fā)生在寫操作之后捂齐。對于一個寫屏障來說,過濾掉不必要的寫操作是十分有必要的缩抡。這種過濾既能加快賦值器的速度辛燥,也能減輕回收器的負(fù)擔(dān)。G1垃圾回收器采用的雙重過濾
- 過濾掉同一個Region內(nèi)部引用缝其;
- 過濾掉空引用挎塌;
過濾掉這兩個部分之后,可以使RS的大小大大減小内边。
G1的垃圾回收器的寫屏障使用一種兩級的log buffer結(jié)構(gòu):
- global set of filled buffer:所有線程共享的一個全局的榴都,存放填滿了的log buffer的集合;
- thread log buffer:每個線程自己的log buffer漠其。所有的線程都會把寫屏障的記錄先放進(jìn)去自己的log buffer中嘴高,裝滿了之后,就會把log buffer放到 global set of filled buffer中和屎,而后再申請一個log buffer拴驮;
可以內(nèi)容可以參閱資料4的11.8節(jié)
Collect Set
Collect Set(CSet)是指,在Evacuation階段柴信,由G1垃圾回收器選擇的待回收的Region集合套啤。G1垃圾回收器的軟實(shí)時的特性就是通過CSet的選擇來實(shí)現(xiàn)的。對應(yīng)于算法的兩種模式fully-young generational mode和partially-young mode随常,CSet的選擇可以分成兩種:
- 在fully-young generational mode下:顧名思義潜沦,該模式下CSet將只包含young的Region。G1將調(diào)整young的Region的數(shù)量來匹配軟實(shí)時的目標(biāo)绪氛;
- 在partially-young mode下:該模式會選擇所有的young region唆鸡,并且選擇一部分的old region。old region的選擇將依據(jù)在Marking cycle phase中對存活對象的計(jì)數(shù)枣察。G1選擇存活對象最少的Region進(jìn)行回收争占。
SATB(snapshot-at-the-beginning)
SATB(snapshot-at-the-beginning),是最開始用于實(shí)時垃圾回收器的一種技術(shù)序目。G1垃圾回收器使用該技術(shù)在標(biāo)記階段記錄一個存活對象的快照("logically takes a snapshot of the set of live objects in the heap at the start of marking cycle")臂痕。然而在并發(fā)標(biāo)記階段,應(yīng)用可能修改了原本的引用宛琅,比如刪除了一個原本的引用刻蟹。這就會導(dǎo)致并發(fā)標(biāo)記結(jié)束之后的存活對象的快照和SATB不一致逗旁。G1是通過在并發(fā)標(biāo)記階段引入一個寫屏障來解決這個問題的:每當(dāng)存在引用更新的情況嘿辟,G1會將修改之前的值寫入一個log buffer(這個記錄會過濾掉原本是空引用的情況)舆瘪,在最終標(biāo)記(final marking phase)階段掃描SATB,修正SATB的誤差红伦。
SATB的log buffer如RS的寫屏障使用的log buffer一樣英古,都是兩級結(jié)構(gòu),作用機(jī)制也是一樣的昙读。
細(xì)節(jié)可以參閱資料2召调,6
Marking bitmaps和TAMS
Marking bitmap是一種數(shù)據(jù)結(jié)構(gòu),其中的每一個bit代表的是一個可用于分配給對象的起始地址蛮浑。舉例來說:
其中addrN代表的是一個對象的起始地址唠叛。綠色的塊代表的是在該起始地址處的對象是存活對象,而其余白色的塊則代表了垃圾對象沮稚。
G1使用了兩個bitmap艺沼,一個叫做previous bitmap,另外一個叫做next bitmap蕴掏。previous bitmap記錄的是上一次的標(biāo)記階段完成之后的構(gòu)造的bitmap障般;next bitmap則是當(dāng)前正在標(biāo)記階段正在構(gòu)造的bitmap。在當(dāng)前標(biāo)記階段結(jié)束之后盛杰,當(dāng)前標(biāo)記的next bitmap就變成了下一次標(biāo)記階段的previous bitmap挽荡。
TAMS(top at mark start)變量,是一對用于區(qū)分在標(biāo)記階段新分配對象的變量即供,分別被稱為previous TAMS和next TAMS定拟。在previous TAMS和next TAMS之間的對象則是本次標(biāo)記階段時候新分配的對象。如圖:
白色region代表的是空閑空間逗嫡,綠色region代表是存活對象办素,橙色region代表的在此次標(biāo)記階段新分配的對象。注意的是祸穷,在橙色區(qū)域的對象性穿,并不能確保它們都事實(shí)上是存活的。
算法詳解
整個算法可以分成兩大部分:
- Marking cycle phase:標(biāo)記階段雷滚,該階段是不斷循環(huán)進(jìn)行的需曾;
- Evacuation phase:該階段是負(fù)責(zé)把一部分region的活對象拷貝到空Region里面去,然后回收原本的Region空間祈远,該階段是STW(stop-the-world)的呆万;
而算法也可以分成兩種模式:
- fully-young generational mode:有時候也會被稱為young GC,該模式只會回收young region车份,算法是通過調(diào)整young region的數(shù)量來達(dá)到軟實(shí)時目標(biāo)的谋减;
- partially-young mode:也被稱為Mixed GC,該階段會回收young region和old region扫沼,算法通過調(diào)整old region的數(shù)量來達(dá)到軟實(shí)時目標(biāo)出爹;
有趣的地方是不論處在何種模式之下庄吼,yong region都在被回收的范圍內(nèi)。而old region只能期望于Mixed GC严就。但是图甜,如同在CMS垃圾回收器中遇到的困境一樣次员,Mixed GC可能來不及回收old region。也就說,在需要分配老年代的對象的時候潮秘,并沒有足夠的空間跺撼。這個時候就只能觸發(fā)一次full GC灭贷。
算法會自動在young GC和mixed GC之間切換削茁,并且定期觸發(fā)Marking cycle phase。HotSpot的G1實(shí)現(xiàn)允許指定一個參數(shù)InitiatingHeapOccupancyPercent粟害,在達(dá)到該參數(shù)的情況下旁理,就會執(zhí)行marking cycle phase。
算法并不使用在對象頭增加字段來標(biāo)記該對象我磁,而是采用bitmap的方式來記錄一個對象被標(biāo)記的情況孽文。這種記錄方法的好處就是在使用這些標(biāo)記信息的時候,僅僅需要掃描bitmap而已夺艰。G1統(tǒng)計(jì)一個region的存活的對象芋哭,就是依賴于bitmap的標(biāo)記。
Marking Cycle Phase
算法的Marking cycle phase大概可以分成五個階段:
- Initial marking phase:G1收集器掃描所有的根郁副。該過程是和young GC的暫停過程一起的减牺;
- Root region scanning phase:掃描Survivor Regions中指向老年代的被initial mark phase標(biāo)記的引用及引用的對象,這一個過程是并發(fā)進(jìn)行的存谎。但是該過程要在下一個young GC開始之前結(jié)束拔疚;
- Concurrent marking phase:并發(fā)標(biāo)記階段,標(biāo)記整個堆的存活對象既荚。該過程可以被young GC所打斷稚失。并發(fā)階段產(chǎn)生的新的引用(或者引用的更新)會被SATB的write barrier記錄下來;
- Remark phase:也叫final marking phase恰聘。該階段只需要掃描SATB(Snapshot At The Beginning)的buffer句各,處理在并發(fā)階段產(chǎn)生的新的存活對象的引用。作為對比晴叨,CMS的remark需要掃描整個mod union table的標(biāo)記為dirty的entry以及全部根凿宾;
- Cleanup phase:清理階段。該階段會計(jì)算每一個region里面存活的對象兼蕊,并把完全沒有存活對象的Region直接放到空閑列表中初厚。在該階段還會重置Remember Set。該階段在計(jì)算Region中存活對象的時候孙技,是STW(Stop-the-world)的产禾,而在重置Remember Set的時候排作,卻是可以并行的;
Initial marking phase
該階段掃描所有的根下愈,與CMS類似纽绍。所不同的是蕾久,該階段是和young GC一起的势似。這里的young GC實(shí)際上是指的就是fully-young generational mode。
Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Guide的原文是"This phase is piggybacked on a normal (STW) young garbage collection"僧著。
Root region scanning phase
該過程主要是掃描Survivor region中指向老年代的履因,在initial mark phase標(biāo)記的引用及其引用的對象。這是一個很奇怪的步驟盹愚,因?yàn)樵谇懊娌徽撌荘arallel Collector還是CMS栅迄,都沒有這么一個步驟。
要理解這一點(diǎn)皆怕,要注意的是毅舆,算法的兩種模式,不論是young GC還是mixed GC愈腾,都需要回收young region憋活。因?yàn)閷?shí)際上RS是不記錄從young region出發(fā)的指針,例如虱黄,這部分指針包括young region - young region悦即,也包括young-region - old region指針。那么就可能出現(xiàn)一種情況橱乱,一個老年代的存活對象辜梳,只被年輕代的對象引用。在一次young GC中泳叠,這些存活的年輕代的對象會被復(fù)制到Survivor Region作瞄,因此需要掃描這些Survivor region來查找這些指向老年代的對象的引用,作為并發(fā)標(biāo)記階段掃描老年代的根的一部分危纫。
在理解了這一點(diǎn)的基礎(chǔ)上粉洼,那么對于階段必須在下一次young GC啟動前完成的要求,也就理解了叶摄。因?yàn)槿绻诙蔚膟oung GC啟動了属韧,那么這個過程中,survivor region就可能發(fā)生變化蛤吓。這個時候執(zhí)行root region phase就會產(chǎn)生錯誤的結(jié)果宵喂。
Concurrent marking phase
在標(biāo)記階段,會使用到一個marking stack的東西会傲。G1不斷從marking stack中取出引用锅棕,遞歸掃描整個堆里的對象圖拙泽,并且在bitmap上進(jìn)行標(biāo)記。這個遞歸過程采用的是深度遍歷裸燎,會不斷把對象的域入棧顾瞻。
在并發(fā)標(biāo)記階段,因?yàn)閼?yīng)用還在運(yùn)行德绿,所以可能會有引用變更荷荤,包括現(xiàn)有引用指向別的對象,或者刪除了一個引用移稳,或者創(chuàng)建了一個新的對象等蕴纳。G1采用的是使用SATB的并發(fā)標(biāo)記算法。
在資料6中記錄了使用SATB的兩條原則:
- All accessible cells at the beginning of the garbage collection are eventually marked during the marked phase;
- Newly alocated cells during the garbage collection are never collected during the sweep phase of that garbage collection
在G1中个粱,該算法的關(guān)鍵在于古毛,如果在并發(fā)標(biāo)記的時候,出現(xiàn)了引用修改(不包含新分配內(nèi)存給對象)都许,那么寫屏障會把這些引用的原始值捕獲下來稻薇,記錄在log buffer中。而后再處理胶征。后續(xù)的所有的標(biāo)記塞椎,都是從原來的值出發(fā),而不是從新的值出發(fā)的弧烤。
SATB是一個邏輯上存在概念忱屑,在實(shí)際中并沒有任何真的實(shí)際的數(shù)據(jù)結(jié)構(gòu)與之對應(yīng)。叫這個名字暇昂,是因?yàn)檩航洌坏┻M(jìn)入了concurrent marking階段,那么該在該階段的運(yùn)行過程中急波,即便應(yīng)用修改了引用从铲,但是因?yàn)镾ATB的寫屏障記錄下來了原始的值,在遍歷整個堆查找存活對象的時候澄暮,使用的依然是原來的值名段。這就是在邏輯上保持了一個snapshot at the beginning of concurrent marking phase。
在處理新創(chuàng)建的對象泣懊,G1采用了不同的方式伸辟。G1用了兩個TAMS變量了判斷新創(chuàng)建的對象。一個叫做previous TAMS馍刮,一個叫做next TAMS信夫。位于兩者之間的對象就是新分配的對象。
并發(fā)標(biāo)記階段,bitmap和TAMS的作用如圖:
注:圖片引自資料2
該圖的詳細(xì)解釋如下:
- A是第一次marking cycle的initial marking階段静稻。next bitmap尚未標(biāo)記任何存活對象警没,而此時的previous TAMS被初始化為region內(nèi)存地址起始值,next TAMS被初始化為top振湾。top實(shí)際上就是一個region未分配區(qū)域和已分配區(qū)域的分界點(diǎn)杀迹;
- B是經(jīng)過concurrent marking階段之后,進(jìn)入了remark階段押搪。此時存活對象的掃描已經(jīng)完成了树酪,因此next bitmap構(gòu)造好了,剛好代表的是當(dāng)下狀態(tài)中region中的內(nèi)存使用情況嵌言。注意的是嗅回,此時top已經(jīng)不再與next TAMS重合了及穗,top和next TAMS之間的就是在前面標(biāo)記階段之時摧茴,新分配的對象;
- C代表的是clean up階段埂陆。C和B比起來苛白,next bitmap變成了previous bitmap,而在bitmap中標(biāo)記為垃圾(也就是白色區(qū)域的)的對應(yīng)的region的區(qū)域也被染成了淺灰色焚虱。這并不是指垃圾對象已經(jīng)被清掃了购裙,僅僅是標(biāo)記出來了。同時next TAMS和previous TAMS也交換了角色鹃栽;
- D代表的是下一個marking cycle的initial marking階段躏率,該階段和A類似,next TAMS重新被初始化為top的值民鼓;
- EF就是BC的重復(fù)薇芝;
Remark phase
該階段是一個STW的階段。引入該階段的目的丰嘉,是為了能夠達(dá)到結(jié)束標(biāo)記的目標(biāo)夯到。要結(jié)束標(biāo)記的過程,要滿足三個條件:
- concurrent marking已經(jīng)追蹤了所有的存活對象饮亏;
- marking stack是空的耍贾;
- 所有的log都被處理了;
前兩個條件是很容易達(dá)到的路幸,但是最后一個是很困難的荐开。如果不引入一個STW的remark過程,那么應(yīng)用會不斷的更新引用简肴,也就是說晃听,會不斷的產(chǎn)生log,因而永遠(yuǎn)也無法達(dá)成完成標(biāo)記的條件。
Clean up
該階段主要完成:
- 統(tǒng)計(jì)存活對象杂伟,這是利用RS和bitmap來完成的移层,統(tǒng)計(jì)的結(jié)果將會用來排序region,以用于下一次的CSet的選擇赫粥;
- 重置RSet观话;
- 把空閑region放到空閑region列表中;
該階段比較容易引起誤解地方在于越平,Clean up并不會清理垃圾對象频蛔,也不會執(zhí)行存活對象的拷貝。也就是說秦叛,在極端情況下晦溪,該階段結(jié)束之后,空閑Region列表將毫無變化挣跋,JVM的內(nèi)存使用情況也毫無變化三圆。
Evacuation
Evacuation階段STW的,大概可以分成兩個步驟:第一個步驟是從Region中選出若干個Region進(jìn)行回收避咆,這些被選中的Region稱為Collect Set(簡稱CSet)舟肉;而第二個步驟則是把這些Region中存活的對象復(fù)制到空閑的Region中去,同時把這些已經(jīng)被回收的Region放到空閑Region列表中查库。
這兩個步驟又可以被分解成三個任務(wù):
- 根據(jù)RS的日志更新RS:只有在處理完了RS的日志之后路媚,RS才能夠保證是準(zhǔn)確的,完整的樊销,這也是Evacuation是STW的重要原因整慎;
- 掃描RS和其余的根來確定存活對象:該階段實(shí)際上最主要依賴于RS;
- 拷貝存活對象:該階段只要從2中確定的根觸發(fā)围苫,沿著引用鏈一直追溯下去裤园,將存活對象復(fù)制到新的region就可以。這個過程中够吩,可能有一部分的年輕代對象會被提升到老年代比然;
Evacuation的時機(jī)
Evacuation的觸發(fā)時機(jī)在不同的模式下會有一些不同。在不同的模式下都相同的是周循,只要堆的使用率達(dá)到了某個閾值强法,就必然會觸發(fā)Evacuation。這是為了確保在Evacuation的時候有足夠的空閑Region來容納存活對象湾笛。
在young GC的情況下饮怯,G1會選擇N個region作為CSet,該CSet首先需要滿足軟實(shí)時的要求嚎研,而一旦已經(jīng)有N個region已經(jīng)被分配了蓖墅,那么就會執(zhí)行一次Evacuation库倘。
G1會盡可能的執(zhí)行mixed GC。唯一的限制就是mix GC也需要滿足軟實(shí)時的要求论矾。
G1觸發(fā)Evacuation的原則大概是:
- 如果被分配的young region數(shù)量滿足young GC的要求教翩,那么就會觸發(fā)young GC;
- 如果被分配的young region數(shù)量不滿足young GC贪壳,就會進(jìn)一步考察加上old region的數(shù)量饱亿,能否滿足old GC的要求;
為了理解這一點(diǎn)闰靴,可以舉例來說彪笼,假如回收一個old region的時間是回收一個young region的兩倍,也就是young region花費(fèi)時間T蚂且,old region花費(fèi)2T配猫,在滿足軟實(shí)時目標(biāo)的情況下,GC只能回收8T的region杏死,那么:
- 假如應(yīng)用現(xiàn)在只分配k(k<8)塊young region泵肄,沒有分配任何old region。這個時候又分配了一個old region识埋,那么這個時候會立刻觸發(fā)一次mixed GC凡伊,此次GC會選擇k塊young region和一塊old region零渐;
- 因此窒舟,在這種假設(shè)下,只要有可以回收的old region的時候诵盼,總是會先回收old region惠豺;
- 在沒有任何old region的情況下,才有可能觸發(fā)young region风宁。
當(dāng)然洁墙,在一般情況下,這些假設(shè)是不成立的戒财。讀者可以思考一下热监,在young GC和mixed GC達(dá)到軟實(shí)時的要求下,young region和old region之間回收的花銷不同會導(dǎo)致young GC和mixed GC會在什么情況下觸發(fā)饮寞。
資料
- Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide
- David Detlefs, Christine Flood, Steve Heller, Tony Printezis. Garbage-First Garbage Collection
- Urs Ho?lzle. A Fast Write Barrier for Generational Collectors
- 垃圾回收算法手冊——自動內(nèi)存管理的藝術(shù)
- 請教G1算法的原理——RednaxelaFX的回答
- Taichi Yuasa. Real-time garbage collection on general-purpose machines.
- Christine H. Flood, David Detlefs, Nir Shavit, and Xiaolan Zhang. Parallel garbage collection for shared memory multiprocessors
- 名詞連接貼-RednaxelaFX對Remeber Set和Card Table的解釋
- G1: One Garbage Collector To Rule Them All
- Poonam Parhar. Understanding G1 GC Logs
- Getting Started with the G1 Garbage Collector