1 與垃圾收集器有關(guān)的算法
在分析G1前先簡單回顧一下與垃圾收集器相關(guān)的算法绞绒。通常所謂的垃圾收集器更多地是指跟蹤垃圾收集器(Tracing Garbage Collection),而不是引用計(jì)數(shù)(Reference Counting )垃圾收集器。跟蹤垃圾收集器采用可達(dá)性分析方法確定哪些對(duì)象要被回收酷愧,通常會(huì)選取一些對(duì)象作為GC Roots,如果對(duì)象能直接或間接地被GC Roots中的對(duì)象引用蹋盆,則認(rèn)為該對(duì)象可達(dá)(存活對(duì)象)不能被回收傀缩,否則該對(duì)象不可達(dá)(垃圾對(duì)象)要被回收。
1.1 三色標(biāo)記算法
在確定內(nèi)存中哪些對(duì)象是垃圾對(duì)象時(shí)屁柏,可以采用最簡單的標(biāo)記算法啦膜,即給內(nèi)存中每個(gè)對(duì)象一個(gè)專門的標(biāo)記位有送,被標(biāo)記則認(rèn)為是存活對(duì)象,否則是垃圾對(duì)象僧家,然從GC Roots的對(duì)象集合開始遞歸遍歷對(duì)象圖雀摘,如果對(duì)象圖中的對(duì)象能被GC Roots中的對(duì)象直接或簡接引用則進(jìn)行標(biāo)記。理論上該算法可以確定內(nèi)存中哪些對(duì)象是存活的八拱,哪些對(duì)象是垃圾阵赠,但整個(gè)過程應(yīng)用程序必須暫停,并且要處理所有的內(nèi)存區(qū)域肌稻。
為了解決上面的問題清蚀,Dijkstra等人在On-the-Fly Garbage Collection: An Exercise in Cooperation 一文中提出了三色標(biāo)記(Tri-color Marking)算法。像Go爹谭、JavaScript 枷邪、Java等語言在內(nèi)存回收上都采用了三色標(biāo)記算法的變種。
三色標(biāo)記算法會(huì)創(chuàng)建白色诺凡、灰色东揣、黑色三個(gè)集合,三個(gè)集合內(nèi)分別只存儲(chǔ)白色對(duì)象腹泌、灰色對(duì)象嘶卧、黑色對(duì)象。白色對(duì)象凉袱,代表尚未開始標(biāo)記的對(duì)象或已完成標(biāo)記并確認(rèn)為垃圾的對(duì)象芥吟;灰色集合,代表還在標(biāo)記中的對(duì)象专甩,即遍歷對(duì)象圖時(shí)已遍歷到自己钟鸵,但還未完成自己引用 對(duì)象的遍歷;黑色對(duì)象涤躲,代表已完成標(biāo)記并確認(rèn)為存活的對(duì)象(正常情況下携添,對(duì)象標(biāo)記的顏色變化只能白色變成灰色,灰色變成黑色)篓叶。起初黑色集合通常為空烈掠,灰色集合內(nèi)為GC Roots直接引用的對(duì)象,其他對(duì)象均在白色集合內(nèi)缸托。一個(gè)對(duì)象任一時(shí)刻只能在白色左敌、灰色、黑色三個(gè)集合中的某一個(gè)俐镐。通常三色標(biāo)記算法的處理流程如下:
1矫限、起初除GC Roots 外的其他對(duì)象全白色集合,將GC Roots直接引用的對(duì)象從白色集合內(nèi)移到灰色集合。
2叼风、從灰色集合取出一個(gè)灰色對(duì)象取董,依次處理該對(duì)象引用的對(duì)象。若其未引用任何對(duì)象无宿,則直接將其移入黑色集合中茵汰;若其引用的對(duì)象在白色集合中則將其移入灰色集合,否則直接不處理孽鸡,當(dāng)該灰色對(duì)象引用的對(duì)象全處理完后蹂午,再將其移入黑色集合中。
3彬碱、重復(fù)第2步的流程直到灰色集合為空豆胸。
4、上面的步驟處理完后巷疼,GC Roots與黑色集合內(nèi)的對(duì)象為存活對(duì)象晚胡,而白色集合內(nèi)的對(duì)象為垃圾對(duì)象,最后要做的就是將白色集合內(nèi)的垃圾對(duì)象清理嚼沿。
下圖展示了除GC Roots 另外有8個(gè)對(duì)象時(shí)搬泥,三色標(biāo)記算法的處理流程。
- 起初除了GC Roots內(nèi)的對(duì)象外伏尼,其他對(duì)象全在右則的白色集合中。
- 將GC Roots直接引用的對(duì)象從白色集合內(nèi)移到灰色集合后尉尾,此時(shí)A對(duì)象與F對(duì)象已從白色集移到灰色集合爆阶。
- 處理完灰色集合中的A對(duì)象引用的B對(duì)象后,此時(shí)B對(duì)象已從白色集移到灰色集合沙咏。
- 處理完灰色集合中的A對(duì)象引用的C對(duì)象與D對(duì)象后辨图。此時(shí)A對(duì)象已從灰色集合移到黑色集合,C對(duì)象與D對(duì)象已從白色集移到灰色集合肢藐。
- 處理灰色集合中剩余的B對(duì)象故河、C對(duì)象、D對(duì)象與F對(duì)象后吆豹,B對(duì)象鱼的、C對(duì)象、D對(duì)象與F對(duì)象象已從灰色集移到黑色集合痘煤。
- 經(jīng)歷過上面的處理后灰色集合已為空凑阶,三色標(biāo)記法標(biāo)記階段結(jié)束到達(dá)清理階段,白色集合中的E對(duì)象衷快、G對(duì)象與H對(duì)象被清理宙橱,最后結(jié)果如下圖。
1.2 三色標(biāo)記算法的不足
如果應(yīng)用程序線程與三色標(biāo)記算法的GC線程一起運(yùn)行,則可能出現(xiàn)對(duì)象錯(cuò)標(biāo)與漏標(biāo)师郑。所謂的對(duì)象錯(cuò)標(biāo)是指原為是垃圾的對(duì)象被標(biāo)記為黑色認(rèn)為是存活的环葵,這種情況的出現(xiàn)并不會(huì)引起應(yīng)用程序的錯(cuò)誤,只是會(huì)將垃圾收集的時(shí)間拖延到下一次垃圾回收宝冕。而對(duì)象漏標(biāo)张遭,則是原本要標(biāo)記為黑色的對(duì)象,被遺漏了猬仁,沒有被標(biāo)記帝璧,最終導(dǎo)致該對(duì)象在白色集合中被垃圾回收集給回收掉;這種情況的一旦發(fā)生應(yīng)用程序?qū)⒊霈F(xiàn)未知的異常湿刽,這個(gè)異车乃福可能是無關(guān)緊要的也可能是致命的。
以上面的例子來看看漏標(biāo)是怎么發(fā)生的诈闺。
假設(shè)GC線程準(zhǔn)備下一步標(biāo)記工作前渴庆,對(duì)象的標(biāo)記狀態(tài)如上圖。此時(shí)GC線程下一步將處理灰色集合中的F對(duì)象雅镊,由于F對(duì)象未引用任何對(duì)象其將直接移動(dòng)到黑色集合中襟雷,整體個(gè)灰色集合為空標(biāo)記結(jié)束。可是如果在GC線程還未完成F對(duì)象從灰色集合轉(zhuǎn)移到黑色集合的操作時(shí)仁烹,應(yīng)用線程正好增加了F對(duì)象對(duì)G對(duì)象的引用呢耸弄?
由于F對(duì)象已結(jié)束標(biāo)記工作(實(shí)際GC線程已認(rèn)為F對(duì)象是黑色的),F(xiàn)對(duì)象最終還是會(huì)從灰色集合成功地轉(zhuǎn)移到黑色集合卓缰。而GC線程將無法感知應(yīng)用程序新增加的F對(duì)象到G對(duì)象的引用计呈,最終導(dǎo)致G對(duì)象的漏標(biāo)歹嘹。實(shí)際上產(chǎn)生漏標(biāo)一定會(huì)滿足下面兩種情況的一種囊嘉。
1、GC線程標(biāo)記的過程中陡厘,應(yīng)用線程增加黑色對(duì)象到白色對(duì)象的引用
2总寒、GC線程標(biāo)記的過程中扶歪,應(yīng)用線程刪除了灰色對(duì)象到白色對(duì)象的引用
上面的漏標(biāo)示例實(shí)際是第一種情況,論文Uniprocessor Garbage Collection Techniques 的 3.2.1 Incremental approache小節(jié)將處理漏標(biāo)時(shí)關(guān)注的點(diǎn)不同將GC分為 Snapshot-at-beginning collectors 與 Incremental update collectors摄闸。Snapshot-at-beginning collectors 關(guān)注于處理第一種情況善镰,而Incremental update collectors關(guān)注于處理第二種情況,G1屬于Snapshot-at-beginning collectors年枕,而CMS屬于Incremental update collectors媳禁。G1并發(fā)標(biāo)記過程關(guān)注處理應(yīng)用線程增加黑色對(duì)象到白色對(duì)象的引用,即當(dāng)黑色對(duì)象新引用了白色對(duì)象時(shí)画切,便將這個(gè)黑色對(duì)象重新設(shè)置為灰(技術(shù)實(shí)現(xiàn)上采用pre-write barrier) 竣稽;而CMS發(fā)標(biāo)記過程關(guān)注處理線程刪除了灰色對(duì)象到白色對(duì)象的引用,即當(dāng)灰色對(duì)象刪除了白色對(duì)象的引用時(shí),便將這個(gè)白色對(duì)象直接置灰(技術(shù)實(shí)現(xiàn)上采用post-write barrier)毫别。
那具體采用何種技術(shù)手段處理上面的兩種情況呢娃弓?其實(shí)也很簡單,就是想辦法讓GC線程感知對(duì)象引用的變化岛宦,即所謂的寫屏障(write barrier)台丛。這里的所說的寫屏障并不是硬件層面的寫屏障,而是軟件層的寫屏障砾肺,其實(shí)質(zhì)可理解為在引用賦值這個(gè)寫操作前加一個(gè)切面挽霉,根據(jù)切點(diǎn)加入時(shí)機(jī)不同又可分為 pre-write barrier 與post-write barrier,下面是G1中采用的寫屏障的偽代碼實(shí)現(xiàn)(來源于[HotSpot VM] 請(qǐng)教G1算法的原理 )变汪。
void oop_field_store(oop* field, oop new_value) {
pre_write_barrier(field); // pre-write barrier: for maintaining SATB invariant
*field = new_value; // the actual store
post_write_barrier(field, new_value); // post-write barrier: for tracking cross-region reference
}
G1中利用pre-write barrier來保證并發(fā)標(biāo)記過程中要處理的SATB(snapshot-at-the-beginning)的完整性(G1 SATB具體如何的實(shí)現(xiàn)后面會(huì)詳細(xì)分析)侠坎,即GC線程在跟蹤標(biāo)記開始階段生成的對(duì)象圖快照時(shí),應(yīng)用線程對(duì)該對(duì)象圖快照的修改能通過pre-write barrier感知裙盾。另一方面G1采用post-write barrier來維護(hù)并發(fā)標(biāo)記過程中應(yīng)用線程新產(chǎn)生的需要跟蹤的跨區(qū)間引用(后面分析G1的RSet時(shí)會(huì)再補(bǔ)充說明)实胸。
1.3 對(duì)象的清除實(shí)現(xiàn)方式
當(dāng)對(duì)象標(biāo)記結(jié)束后,便可以清除對(duì)象番官。而在具體實(shí)現(xiàn)時(shí)可以采用三種方式標(biāo)記清除庐完、標(biāo)記復(fù)制、標(biāo)記壓縮算法徘熔。標(biāo)記清除最為簡單與高效门躯,其直接將那些垃圾對(duì)象清除,但這也帶來了內(nèi)存碎片的問題酷师,同時(shí)對(duì)象分配時(shí)也不得不采用空閑空間列表算法而不能采用高效的指針碰撞算法讶凉。標(biāo)記復(fù)制算法通常要額外占用50%的空間,其實(shí)現(xiàn)是一直用一半內(nèi)存存儲(chǔ)對(duì)象窒升,而另一半內(nèi)存置空,當(dāng)回收垃圾時(shí)慕匠,將已使用空間中仍存活的對(duì)象直接復(fù)制到置空的那段內(nèi)存中饱须,然后直接置空之前使用的那半內(nèi)存。標(biāo)記壓縮算法台谊,兼顧標(biāo)記清除與標(biāo)記復(fù)制算法的優(yōu)點(diǎn)蓉媳,在回收垃圾后會(huì)對(duì)存活對(duì)象進(jìn)行相應(yīng)的移動(dòng),盡量將碎片化的內(nèi)存空間進(jìn)行壓縮锅铅。
2 分代垃圾收集器
在分析G1垃圾收集器之前有必要先簡單回顧一下HotSpot VM中的其他垃圾收集器酪呻。在G1出現(xiàn)之前HotSpot VM中的其他垃圾收集器都是基于新生代與年老代進(jìn)行垃圾回收的,這些垃圾回收器集統(tǒng)稱為分代垃圾盐须。而G1則是兼顧分代與分區(qū)的垃圾收集器玩荠。
2.1 分代垃圾收集器垃圾收集過程
分代垃圾收集器將Heap劃分為新生代(Young Generation)與年老代 (Old Generation),在JDK1.8 之前還有永久代(Permanent Generation)的概念。新生代又被進(jìn)一步劃分為Eden阶冈、From Space 闷尿、To Space,其中 From Space 與 To Space 大小相等又稱作Survivor Spaces女坑。
Heap被劃分為新生代與年老代是基于弱分代假設(shè)的填具,在java應(yīng)用程序與其他應(yīng)用程序中都可以觀測到弱分代假設(shè)的現(xiàn)象。
1匆骗、大多數(shù)分配的對(duì)象不會(huì)被長期引用(被認(rèn)為是存活的)即他們很年輕就死去劳景。
2、老對(duì)象很少持有來自新對(duì)象的引用碉就。
新生代垃圾回收相對(duì)頻繁盟广,且利用的算法高效快速,因?yàn)槟贻p代空間通常很小并且可能包含許多不再被引用的對(duì)象铝噩。而年老代垃圾回收頻率則相對(duì)較低衡蚂,但由于年老代占用內(nèi)存相對(duì)更多,通常老代垃圾回收將更加耗時(shí)骏庸。新生代與年老代分別存儲(chǔ)不同年齡的對(duì)象毛甲,通常剛分配內(nèi)存的對(duì)象被存儲(chǔ)在新生代,每經(jīng)過一次垃圾回收如果對(duì)象還存活其年齡將加1具被,當(dāng)經(jīng)過多輪垃圾回收后如果對(duì)象的年齡超過了MaxTenuringThreshold值玻募,該對(duì)象將晉升到年老代。
由于新生代垃圾回收相對(duì)更加頻繁一姿,新生代垃圾回收更加關(guān)注垃圾回收的時(shí)效性七咧,通常會(huì)采用復(fù)制算法或標(biāo)記清除算法處理垃圾回收。年老代占用內(nèi)存相對(duì)更大叮叹,而垃圾回收頻繁較低艾栋,年老代垃圾回收更加關(guān)注垃圾回收的空間性,即垃圾回收后能否釋放更多連續(xù)的內(nèi)存蛉顽,通常會(huì)采用壓縮算法處理垃圾回收蝗砾。
現(xiàn)在來簡單看看對(duì)象如何在Eden、Survivor與Old Generation之間進(jìn)行分配與轉(zhuǎn)移的携冤。
- 任何新對(duì)象都被分配到新生代的Eden空間悼粮,當(dāng)Eden區(qū)域無法容納新對(duì)象時(shí),會(huì)觸發(fā)一次Young GC曾棕。 最開始時(shí)兩個(gè)Survivor空間都是空(下圖是已經(jīng)過若干次GC的情況)扣猫。
- Young GC 過程中Eden空間仍被引用的對(duì)象(存活對(duì)象)會(huì)被復(fù)制到第一個(gè)Survivor空間(S0 Survivor Space)。 而Eden 空間未被引用的對(duì)象將被直接刪除翘地。經(jīng)歷過一次Young GC后仍存活的對(duì)象申尤,其年齡都會(huì)增加1癌幕,下圖S0 Survivor Space中的對(duì)象都只經(jīng)歷一次Young GC,全被標(biāo)記為1瀑凝。
- 下一次Young GC 中仍被引用的對(duì)象(存活對(duì)象)會(huì)被復(fù)制到之前是空的Survivor空間(To survivor space 序芦,實(shí)際是之前的S1 survivor space),Eden 空間未被引用的對(duì)象將被直接刪除粤咪。 之前的S0 suvivor space現(xiàn)在稱為Form survivor space谚中,其中依賴被引用的對(duì)象,被復(fù)現(xiàn)到了之前是空的Survivor空間(To survivor space )寥枝,F(xiàn)orm survivor space 未被引用的對(duì)象將被直接刪除宪塔。仍被引用的對(duì)象從Form survivor space復(fù)制到To survivor space后,其對(duì)象年齡將加1囊拜,表明該對(duì)象又經(jīng)歷了一次Young GC某筐。
- 再下一次Young GC 中,會(huì)重復(fù)上面相同的過程冠跷。 但這時(shí)Survivor space 角色將進(jìn)行交換南誊,即From survivor space 變成 To survivor space,To survivor space 變成 From survivor space蜜托。這個(gè)交換的目的實(shí)際就是為了將已使用的Survivor space中仍存活的對(duì)象復(fù)制到被清空的Survivor space中抄囚。
- 當(dāng)經(jīng)歷很多次Young GC后新生代中仍存活的對(duì)象將會(huì)晉升(Promotion)到老年代。下圖展示了當(dāng)MaxTenuringThreshold參數(shù)為8 時(shí)橄务,仍存活的對(duì)象從新生代的From survivor space晉升到老年代幔托。
- 隨著Young GC 的不斷發(fā)生,新生代中仍存活的對(duì)象將不斷地晉升到年老代蜂挪。最終老年代將沒有更多的空間容納新晉升的對(duì)象重挑,此時(shí)引發(fā)Major GC。
對(duì)象分配與晉升時(shí)何時(shí)會(huì)觸發(fā)GC的詳細(xì)流程圖可以參考下圖(參考了《碼出高效:Java開發(fā)手冊(cè)》第四章走進(jìn)JVM中的圖):
上圖中沒有描繪出 Thread Local Allocation Buffer (TLAB)與 Promotion Local Allocation Buffer (PLAB)的細(xì)節(jié)棠涮。此外上圖中的Full GC可能讓大家引起歧義谬哀,因?yàn)楹蚆ajor GC太容易混淆了。實(shí)際JVM規(guī)范與垃圾收回相關(guān)的文獻(xiàn)并沒有給Full GC 與 Major GC作定義严肪。一般Full GC認(rèn)為是對(duì)新生代與老年代都進(jìn)行垃圾回收史煎,而Major GC則是專門針對(duì)年老代垃圾進(jìn)行回收。那問題來了由Young GC 引發(fā)了老年代的垃圾回收诬垂,是叫Full GC好呢劲室,還是Major GC好呢伦仍?個(gè)人認(rèn)為可能Full GC更合適结窘,這個(gè)大家可以不用過多糾結(jié)這個(gè)。實(shí)現(xiàn)糾結(jié)可以看看這兩篇文章Minor GC vs Major GC vs Full GC 與 Major GC和Full GC的區(qū)別是什么充蓝?觸發(fā)條件呢隧枫?喉磁。
上面只簡單的描述了分代垃圾收集器垃圾收集的過程,實(shí)際垃圾收集器不僅負(fù)責(zé)了內(nèi)存的回收工作官脓,同樣負(fù)責(zé)了對(duì)象的分配工作协怒。更多的入門內(nèi)容可以參考Memory Management in the Java HotSpot? Virtual Machine 、Plumbr Handbook Java Garbage Collection卑笨。如果想再進(jìn)一步了解垃圾回收相關(guān)的東西孕暇,還可以看看 《垃圾回收算法手冊(cè) 自動(dòng)內(nèi)存管理的藝術(shù)》。
2.2 串行垃圾收集器
串行垃圾收集器(Serial GC)在進(jìn)行垃圾回收時(shí)只有單個(gè)GC線程在進(jìn)行垃圾回收赤兴。通常實(shí)現(xiàn)串行垃圾回收器更加簡單妖滔,串行垃圾回收器內(nèi)部不用維護(hù)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),內(nèi)存開銷也更加小桶良。但由于在STW(Stop The World)時(shí)只有單個(gè)GC線程在進(jìn)行垃圾回收工作座舍,垃圾回收的時(shí)間通常都會(huì)比較長,并且與應(yīng)用程序占用的內(nèi)存呈線性增長陨帆。該垃圾回收器比較適合Client端與嵌入設(shè)備等占用內(nèi)存較小的場景曲秉。
上圖灰色箭頭為應(yīng)用線程,而黑色箭頭為GC線程疲牵,應(yīng)用線程在工作時(shí)通常都是多線程承二,而到過安全點(diǎn)后應(yīng)用線程停止工作也叫SWT(Stop The World),串行垃圾回收器將開始一個(gè)GC線程完成垃圾回收工作瑰步。根據(jù)回收分代的不同串行垃圾回收器通常又分為Serial New 與 Serial Old矢洲,他們分別負(fù)責(zé)回收新生代(Young Generation)與年老代(Old Generation)。Serial New采用復(fù)制算法完成垃圾清理工作缩焦,Serial Old采用壓縮算法完成垃圾清理工作读虏。
2.3 并行垃圾收集器
很顯然在多核CPU架構(gòu)下面垃圾回收時(shí),串行垃圾回收器不能利用多核CPU的優(yōu)勢袁滥。因此出行了并行垃圾收集器(Parallel GC)盖桥,其與串行垃圾回收器最大的差別在于STW時(shí),進(jìn)行垃圾回收的線程由單個(gè)變成了多個(gè)题翻。相對(duì)于串行垃圾回收器而言揩徊,由于垃圾回收的工作被分配給了多個(gè)線程,每次進(jìn)行GC時(shí)整體時(shí)間將大大下降嵌赠。并行垃圾回收器工作時(shí)塑荒,新生代與年老代都會(huì)采用線程并行處理垃圾回收工作。與串行垃圾回收器一樣姜挺,并行垃圾回收器齿税,根據(jù)回收分代的不同通常又分為ParNew 與 Parallel Old,他們分別負(fù)責(zé)回收新生代(Young Generation)與年老代(Old Generation)炊豪。同樣新生代垃圾回收采用復(fù)制算法完成垃圾清理工作凌箕,年老代采用壓縮算法完成垃圾清理工作拧篮。
上圖灰色箭頭為應(yīng)用線程,而黑色箭頭為GC線程牵舱,并行垃圾回收器在STW時(shí)進(jìn)行垃圾回收的線程相對(duì)于串行垃圾回收器而言變成了多個(gè)線程串绩,并且這些線程同時(shí)進(jìn)行垃圾回收工作。
2.4 并發(fā)標(biāo)記清除垃圾收集器
并發(fā)標(biāo)記清除垃圾收集器 (Concurrent Mark Sweep芜壁,CMS )是Hotspot VM上真正意義上的并發(fā)垃圾回收器礁凡。所謂并發(fā)(Concurrent)是指GC線程與應(yīng)用線程一起工作,GC線程工作時(shí)不用STW慧妄,應(yīng)用線程也在工作把篓,而通常說的并行(Parallel)是指多個(gè)GC線程同時(shí)工作,清理垃圾腰涧。很多文獻(xiàn)中將應(yīng)用線程叫作Mutator Thread韧掩。
CMS 主要負(fù)責(zé)回收年老代垃圾,使用CMS時(shí)新生代垃圾收集工作通常由Serial New 或 ParNew 完成窖铡,默認(rèn)新生代垃圾回收器為ParNew疗锐。CMS回收年老代垃圾時(shí),將整體垃圾回收的過程拆分為多個(gè)階段费彼,并且大部分階段與應(yīng)用線程都是并發(fā)不會(huì)發(fā)生STW滑臊。CMS整體垃圾回收過程可分為初始化標(biāo)記( Initial-mark)、并發(fā)標(biāo)記(Concurrent Marking)箍铲、并發(fā)預(yù)清除(Concurrent Pre-cleaning)雇卷、重新標(biāo)記(Remark)、并發(fā)清除(Concurrent Sweeping)颠猴,初始化標(biāo)記與重新標(biāo)記都會(huì)發(fā)生STW关划,但通常時(shí)間都比較短。CMS早其版本中初始化標(biāo)記與重新標(biāo)記都是由單線程完成的翘瓮,后期版本可以通過 -XX:+CMSParallelInitialMark 與 -XX:CMSParallelRemarkEnabled 分別將初始化標(biāo)記與重新標(biāo)記階段指定為多線程贮折。在CMS對(duì)年老代進(jìn)行并發(fā)回收時(shí)很多可能新生代發(fā)生了Young GC,此時(shí)年老代垃圾回收將立刻中斷资盅,直到Y(jié)oung GC結(jié)束后又重新恢復(fù)调榄。
上圖灰色箭頭為應(yīng)用線程,而黑色箭頭為GC線程呵扛,CMS在Initial-mark階段開啟多個(gè)GC線程對(duì)GC Root進(jìn)行標(biāo)記每庆,該階段通常時(shí)間會(huì)比較短。CMS在并發(fā)標(biāo)記與并發(fā)預(yù)清除階段同會(huì)開啟多線程工作今穿,該階段GC線程與應(yīng)用線程并發(fā)工作缤灵。上圖中Concurrent Making Pre-cleaning 階段中長的黑色箭頭代表處理Concurrent Making工作的GC線程,短的黑色箭頭代表處理Pre-cleaning工作的線程荣赶。CMS在重新標(biāo)記同樣開啟多個(gè)GC線程并且與Initial-mark階段一樣會(huì)SWT凤价。CMS在并發(fā)清除階段GC線程與應(yīng)用線程并發(fā)工作。
CMS調(diào)優(yōu)的一個(gè)關(guān)鍵問題是如何找出合適的時(shí)間讓CMS開始并發(fā)工作拔创,以便在應(yīng)用程序耗盡可用的堆空間之前CMS完成所有的并發(fā)工作利诺。通常會(huì)某次Young GC后開始CMS的并發(fā)工作,因?yàn)閅oung GC過后 CMS Initail-mark 要標(biāo)記的對(duì)象通常會(huì)更少剩燥。CMS另外的一個(gè)問題是年老代內(nèi)存碎片問題慢逾,由于CMS在回收年老代時(shí)采用了標(biāo)記清除算法,標(biāo)記清除算法相對(duì)于壓縮算法而言執(zhí)行效率更高灭红,但由于清理垃圾時(shí)沒有對(duì)內(nèi)存的壓縮整理侣滩,其不可避免地會(huì)出現(xiàn)內(nèi)存碎片問題。下面二種情況會(huì)由于內(nèi)存碎片問題最終導(dǎo)致concurrent mode failure变擒。
1君珠、Young GC 時(shí),Eden區(qū)域存活的對(duì)象過大Survivor區(qū)域無法存放導(dǎo)致promotion failed娇斑,此時(shí)對(duì)象只能放入年老代策添,但由于內(nèi)存碎片問題年老代同樣放不下該對(duì)象,最后將發(fā)生concurrent mode failure毫缆,這時(shí)會(huì)引發(fā)Full GC唯竹,F(xiàn)ull GC會(huì)回收整個(gè)Heap 空間導(dǎo)致STW時(shí)長驟增。
2苦丁、Young GC 時(shí)浸颓,Survivor 區(qū)域存活對(duì)象年齡超過了MaxTenuringThreshold,晉升到年老代旺拉,但由于內(nèi)存碎片問題年老代放不下該對(duì)象产上,將發(fā)生concurrent mode failure,這時(shí)會(huì)引發(fā)Full GC蛾狗。
更多關(guān)于CMS調(diào)優(yōu)方面的實(shí)踐可以參考這兩篇文章 Java中9種常見的CMS GC問題分析與解決 與 Understanding GC pauses in JVM, HotSpot's CMS collector
下面是一張關(guān)于HotSpot VM 中垃圾回收器如何組合分別處理年輕代與年老代的經(jīng)典圖蒂秘。上面部分的Serial New、ParNew淘太、Parallel Scavenge 都是專門用于處理新生代垃圾收集器姻僧,下面部分的CMS、Serial Old蒲牧、Parallel Old是專門用于處理年老代的垃圾收集器撇贺,而處于中間的G1即能處理新生代也能處理年老代。圖中的黑色實(shí)線代表哪些新生代垃圾收集器能與哪些年老代垃圾收集器組合工作冰抢。CMS與Serial Old之間的黑色虛線代表CMS發(fā)生concurrent mode failure時(shí)fail safe成Full GC采用Serial Old回收年老代垃圾松嘶。
上面提到的Serial GC(Serial New 與 Serial Old)、Parallel GC(ParNew挎扰、Parallel Scavenge翠订、Parallel Old)巢音、CMS,由于新生代與年老代其內(nèi)存布局是連續(xù)的(虛似內(nèi)存是連續(xù)的)這些垃圾收集器在回收垃圾時(shí)要么只能處理具體某一個(gè)分區(qū)要么只能處理整個(gè)Heap尽超。這必然會(huì)導(dǎo)致垃圾回收的STW時(shí)間或多或少與應(yīng)用程序占用內(nèi)存線性正相關(guān)官撼,即應(yīng)用程序占用的內(nèi)存越大在執(zhí)行垃圾回收時(shí)STW時(shí)間將越久。前面的垃圾收集器都是分代的垃圾收集器似谁,G1開啟了分區(qū)垃圾收集器的先河(雖然G1在邏輯上也有新生代與年老代的概念)傲绣。G1利用分治的思想將整體Heap劃分為一塊塊大小相等的Region,在內(nèi)存管理時(shí)可以針對(duì)這些Region進(jìn)行管理巩踏,而不是籠統(tǒng)地對(duì)某個(gè)Generation進(jìn)行管理秃诵。由于Region的大小通常遠(yuǎn)小于Generation,垃圾回收時(shí)處理多個(gè)Region效率通常高于處理某個(gè)Generation塞琼。
3 G1垃圾收集器概述
G1(Garbage First)垃圾收集器是續(xù)CMS收集器后的另一款跨時(shí)代的垃圾收集器菠净,其開啟了分區(qū)垃圾收集器的先河。G1通過時(shí)間預(yù)測模型盡可能地滿足用戶對(duì)暫停時(shí)間的要求(用戶可以通過-XX:MaxGCPauseMillis=XXX彪杉,來指定垃圾收回時(shí)最大的暫停時(shí)間)嗤练,G1 利用壓縮算法優(yōu)化回收垃圾更多的分區(qū),所以他被稱作垃圾優(yōu)先(Garbage First)垃圾收集器在讶。
3.1 G1 垃圾收集中的內(nèi)存布局
G1與上面介紹的傳統(tǒng)分代垃圾收集器一樣同樣存在Eden Generation煞抬、Survivor Generation、Old Generation的概念构哺,但與他們最大的區(qū)別在于這些Generation的關(guān)系是邏輯上的關(guān)系革答,其各Generation內(nèi)存布局不會(huì)存在連續(xù)性。G1 將Heap劃分為一個(gè)個(gè)Region曙强,每個(gè)Region的大小為2的N次方残拐,其值在1M到32M之間。每一個(gè)Region屬于某個(gè)Generation碟嘴,于是有了Eden Region溪食、Survivor Region、Old Region/Tenured Region的概念(不像傳統(tǒng)分代垃圾收集器娜扇,G1中沒有From Survivor 與 To Survivor的概念错沃, 因?yàn)镚1不管是Young GC、Mix GC雀瓢、Full GC 對(duì)象都是從一個(gè)Region轉(zhuǎn)移到另外一個(gè)Region或是直接清除)枢析。除此之外G1還有一個(gè)專門用于存放大對(duì)象的Region(默認(rèn)對(duì)象占用內(nèi)存超過Region大小二分之一的對(duì)象),稱為Humongous Region刃麸,Humongous Region 可能由多個(gè)Region構(gòu)成醒叁,但一個(gè)Region最多存放一個(gè)大對(duì)象,當(dāng)多個(gè)Region用于存放一個(gè)特別大的對(duì)象這些Region內(nèi)在布局上是連續(xù)的。當(dāng)經(jīng)過多次Young GC把沼、Mix GC啊易、Full GC與對(duì)象分配后(G1中Young GC、Mix GC饮睬、Full GC 相關(guān)的東西后面會(huì)涉及)租谈,Eden Region、Survivor Region续捂、Old Region、Humongous Region之間的角色會(huì)轉(zhuǎn)變宦搬,即原來存有具體某種Generation對(duì)象的Region被清空后可以用來存放Eden對(duì)象牙瓢、Survivor對(duì)象、Old 對(duì)象或是Humongous對(duì)象中的某一種间校。
這種將內(nèi)存分為一個(gè)個(gè)Region的內(nèi)存布局更加有利于內(nèi)存的回收矾克,垃圾回收集可以采用分治的思想去管理一小塊的內(nèi)存(處理內(nèi)存的分配與回收),避免了之前版本垃圾回收集在處理Old Generation時(shí)只能處理整個(gè)Old Generation困局(整個(gè)Old Generation一起處理通常非常耗時(shí)的憔足,而且這個(gè)過程中避免不了STW)胁附。
3.2 G1 垃圾收集的周期
從全局視角來看,G1收集器回收垃圾的過程是在Young-only 階段與Space Reclamation階段之間進(jìn)行交替的滓彰,下圖來源于Oracle官網(wǎng)HotSpot Virtual Machine Garbage Collection Tuning Guide 一文中控妻。
- Young-only 階段
Young-only階段實(shí)際包括了多次Young GC 與整個(gè)并發(fā)標(biāo)記過程。其從一些普通的Young GC(上圖中小的藍(lán)色點(diǎn)代表普通的Young GC)開始揭绑,并將滿足條件的對(duì)象提升到老年代弓候。當(dāng)年老代占用內(nèi)存超過閾值時(shí),會(huì)觸發(fā)并發(fā)標(biāo)記階段他匪,該閾值由參數(shù)-XX:InitiatingHeapOccupancyPercent=65%菇存,指定默認(rèn)值為65%。與此同時(shí)G1會(huì)開啟并發(fā)的Young GC(上圖中大的藍(lán)色大代表并發(fā)的Young GC) 邦蜜,而不是普通的Young GC依鸥。整個(gè)并發(fā)標(biāo)記階段是與普通的Young GC交替的。并發(fā)標(biāo)記階段又可細(xì)分為初始化標(biāo)記(Initial Marking)悼沈、重新標(biāo)記(Remark)與清理(Cleanup)階段贱迟。初始化標(biāo)記階段實(shí)際是在并發(fā)的Young GC中完成的(文獻(xiàn)中通常用piggybacking一詞表述)关筒。當(dāng)初始化標(biāo)記完成后可能會(huì)發(fā)生若干的普通Yong GC,才進(jìn)入Remark階段(上圖中靠上方的黃色小點(diǎn))。接著并發(fā)標(biāo)記可能被Young GC打斷宿崭,Young GC結(jié)束后再進(jìn)入Cleanup階段(上圖中靠下方的黃色小點(diǎn))。 - Space Reclamation 階段
當(dāng)Cleanup結(jié)束后,G1會(huì)進(jìn)入Space Reclamation 階段洲守,該階段由若干次的MixGC組成撒蟀。每次MixGC都會(huì)從之前并發(fā)標(biāo)記階段標(biāo)記的對(duì)象中選擇一部分進(jìn)行清理唉俗,MixGC過程中同時(shí)伴隨著部分的Young GC(上圖中紅色的小點(diǎn)代表一次MixGC)。當(dāng)G1發(fā)現(xiàn)清除對(duì)象所獲取的空間不夠多時(shí)將停止MixGC配椭,與此同時(shí)Space Reclamation 階段結(jié)束虫溜。
當(dāng)Space Reclamation 階段結(jié)束后,G1收集周期又重一個(gè)Young-only階段重新開始股缸。Young-only中的普通Young GC的觸發(fā)條件與前分的分代垃圾收集器Young GC觸發(fā)條件一致,只不過G1是針對(duì)Region處理的瘾境,即G1會(huì)根據(jù)Eden Region中是否有Region能夠容納新對(duì)象來決定是否要開啟Young GC迷守。作為兜底策略兑凿,當(dāng)G1垃圾回收過程釋放的內(nèi)存不足于滿足應(yīng)用程序中新對(duì)象對(duì)內(nèi)存要求時(shí)凯力,G1會(huì)采用Full GC處理所有Region咐鹤。
3.3 記憶集(RSet)
前面已了解到Y(jié)oung GC時(shí)只會(huì)處理新生代對(duì)應(yīng)的Region即 Eden Region與Survivor Region疹蛉,這有利于降低每次Young GC的時(shí)間座慰。但如果 Eden Region與Survivor Region持有老年代的引用呢然想,難道在Young GC時(shí)狠半,要把Heap中所有的Region都遍歷一次才能確定Eden Region與Survivor Region有哪些對(duì)象才是垃圾嗎偶妖?這種方式顯然是不可取的,這樣一來就會(huì)拉長Young GC的時(shí)間。
有種有效地方法是新生代的每一個(gè)Region都維護(hù)一個(gè)集合記錄一下老年代指進(jìn)來的(point-in)的跨代引用奖慌,這樣在Young GC時(shí)只要看一下這個(gè)point-in的集合就行抛虫,這個(gè)集合便是所謂的記憶集(Remember Set伞矩,RSet)。那年老代里面需要這個(gè)RSet嗎夏志?前面提到每次Mix GC時(shí)會(huì)回收部分年老代的Region,如果沒有這個(gè)記憶集的話和Young GC一樣同樣避免不了要掃描整個(gè)年老代的Region盲镶,所以年老代的Region也要維護(hù)一個(gè)point-in的集合侥袜,不過個(gè)集合記錄是Old Region point-in 過來的集合,至于Young Region point-in 過來的則可以不用管溉贿。
那RSet具體實(shí)現(xiàn)上又是怎么樣的呢枫吧?在這之前必須先知道卡表(CardTable),在G1之前CMS中也有CardTable宇色。CardTable本質(zhì)上是一種point-out數(shù)據(jù)結(jié)構(gòu)九杂,表示某一區(qū)域自己有指向別的區(qū)域的引用颁湖。在G1中CardTable由byte數(shù)組構(gòu)成,數(shù)組的每個(gè)元素稱之為卡片/卡頁(CardPage)例隆。CardTale會(huì)映射到整個(gè)堆的空間甥捺,每個(gè)CardPage會(huì)對(duì)應(yīng)堆中的512B空間。如下圖所示镀层,在一個(gè)大小為8GB的堆中镰禾,那么CardTable的長度為16777215 (8GB / 512B);假設(shè)-XX:G1HeapRegionSize參數(shù)為2MB唱逢,即每個(gè)Region 大小為2 MB吴侦,則每個(gè)Region都會(huì)對(duì)應(yīng)4096個(gè)CardPage。CardTable將占用16MB額外內(nèi)存空間坞古。
查找一個(gè)對(duì)象所在的CardPage只需要應(yīng)用如下公式便可得出备韧。
說完CardTable再來看看RSet的具體實(shí)現(xiàn),RSet實(shí)際是通過HashMap實(shí)現(xiàn)的痪枫,該HashMap其key引用了本Region的其他Regionr的地址织堂,value是一個(gè)數(shù)組,數(shù)組的元素是引用方的對(duì)象所對(duì)應(yīng)的CardPage在CardTable中的下標(biāo)奶陈。
如上圖所示易阳,區(qū)域B中的對(duì)象y引用了區(qū)域A中的對(duì)象x,這個(gè)引用關(guān)系跨了兩個(gè)區(qū)域尿瞭。y對(duì)象所在的CardPage為179闽烙,在區(qū)域A的RSet中翅睛,以區(qū)域B的地址作為key声搁,b對(duì)象所在CardPage下標(biāo)79為value記錄了這個(gè)引用關(guān)系,這樣就完成了這個(gè)跨區(qū)域引用的記錄捕发。不過這個(gè)CardTable的粒度有點(diǎn)粗疏旨,畢竟一個(gè)CardPage有512B,在一個(gè)CardPage內(nèi)可能會(huì)存在多個(gè)對(duì)象扎酷。所以在掃描標(biāo)記時(shí)檐涝,需要掃描RSet中關(guān)聯(lián)的整個(gè)CardPage,上圖的例子是要把CardTable下標(biāo)為79的CardPage都掃描一遍法挨。
實(shí)際上HotSpot VM 中 G1的RSet具體實(shí)現(xiàn)要比上面說的更加復(fù)雜(上面說的只是其中的一種情況谁榜,Sparse粒度的情況 )。應(yīng)用程序中可能存在頻繁的更新引用情況凡纳,這會(huì)使得某些區(qū)域的RSet變成popular Region窃植。G1 采用不同粒度的方式來處理RSet Popularity,RSet可分為Sparse荐糜、Fine巷怜、Coarse三種粒度葛超。不同粒度時(shí)RSet內(nèi)部采用不同的數(shù)據(jù)結(jié)構(gòu)記錄其他Region point-in 進(jìn)來的引用 ,上面介紹的便是Sparse粒度時(shí)的情況延塑。下面是 Evaluating and improving remembered sets in the HotSpot G1 garbage collector論文中給出的G1中 RSet 數(shù)據(jù)結(jié)構(gòu)的簡化定義绣张。
1、Sparse Grained (上面g1_rset數(shù)據(jù)結(jié)構(gòu)中的 saprse)
稀疏粒度情況時(shí)关带,采用HashMap實(shí)現(xiàn)侥涵,該HashMap其key引用了本Region的其他Regionr的地址,value是一個(gè)數(shù)組宋雏,數(shù)組的元素是引用方的對(duì)象所對(duì)應(yīng)的CardPage在CardTable中的下標(biāo)独令。
2、Fine Grained (上面g1_rset數(shù)據(jù)結(jié)構(gòu)中的 fine_grained)
細(xì)粒度情況時(shí)好芭,同樣采用HashMap實(shí)現(xiàn)燃箭,該HashMap其key引用了本Region的其他Regionr的地址,value是一個(gè)位圖舍败,位圖的最大位數(shù)代表一個(gè)Region最多能被拆分為多少CardPage招狸,位圖上值為1則代表Region上CardPage內(nèi)有對(duì)象引用了RSet 所屬Region的對(duì)象。
3邻薯、Coarse Grained (上面g1_rset數(shù)據(jù)結(jié)構(gòu)中的 coarse)
粗粒度情況時(shí)裙戏,采用位圖實(shí)現(xiàn),位圖的最大位數(shù)代表整個(gè)Heap能被拆分為多少個(gè)Region厕诡。位圖上值為1則代表其他Region內(nèi)有對(duì)象引用了RSet 所屬Region的對(duì)象累榜。因?yàn)镽egion的大小是一樣的,可以通過Heap的起始地址灵嫌,計(jì)算出位圖中每個(gè)Region的起始地址壹罚。
G1通常利用Refinement Threads 異步維護(hù)RSet,每個(gè)線程會(huì)利用前面介紹的post-write barrier 將跨代引用與Old Generation 到 Old Generation 的引用記錄到各自的local log buffer中寿羞,當(dāng)local log buffer滿了之后會(huì)刷新到全局的 log buffer中猖凛,Refinement Threads 專門處理全局的 log buffer來維護(hù)RSet,當(dāng)Refinement Threads 不能有效地處理全局的log buffer時(shí)绪穆,應(yīng)用線程將一起處理 log buffer辨泳,但這對(duì)應(yīng)用線程的性能有損耗。當(dāng)垃圾回收過程中如果全局的log buffer還未處理完玖院,GC線程將處這些log buffer菠红。
void oop_field_store(oop* field, oop new_value) {
pre_write_barrier(field); // pre-write barrier: for maintaining SATB invariant
*field = new_value; // the actual store
post_write_barrier(field, new_value); // post-write barrier: for tracking cross-region reference
}
3.4 回收集(CSet)
回收集(Collection Set,CSet)难菌,其代表每次GC暫停時(shí)回收的一系列目標(biāo)分區(qū)试溯。在任意一次收集暫停中,CSet所有分區(qū)都會(huì)被釋放扔傅,內(nèi)部存活的對(duì)象都會(huì)被轉(zhuǎn)移到分配的空閑分區(qū)中耍共。因此無論是年輕代收集烫饼,還是混合收集,工作的機(jī)制都是一致的试读。年輕代收集CSet只容納年輕代分區(qū)杠纵,而混合收集會(huì)通過啟發(fā)式算法,在老年代候選回收分區(qū)中钩骇,篩選出回收收益最高的分區(qū)添加到CSet中比藻。
4 深入分析G1垃圾收集
前面已簡要地介紹了,G1中Heap的內(nèi)存布局倘屹、全局視角下G1的周期银亲、RSet具體實(shí)現(xiàn)、CSet等內(nèi)容纽匙,下再更細(xì)致地介紹一下G1中的Young GC 階段务蝠、并發(fā)標(biāo)記階段、Mix GC 階段烛缔。
4.1 Young GC階段
同分代垃圾回收器一樣馏段,當(dāng)G1中沒有Eden Region能夠容納新要?jiǎng)?chuàng)建的對(duì)象時(shí),G1中Young GC被觸發(fā)践瓷;同時(shí)每個(gè)線程都有對(duì)應(yīng)的TLAB院喜,小的對(duì)象優(yōu)先直接在TLAB中創(chuàng)建。前面已了解到G1的Young GC階段只會(huì)回收全部的Young Region晕翠,Eden Region 與 Survivor Region喷舀;同時(shí)如果年老代內(nèi)存占比超過了指定的閾值時(shí),Young GC會(huì)一同完成并發(fā)標(biāo)階段的初始化標(biāo)記工作淋肾。每次Young GC后硫麻,G1會(huì)根據(jù)當(dāng)前新生代大小、新生代最小值巫员、新生代最大值庶香、目標(biāo)暫停時(shí)間等重新調(diào)整新生代的大小甲棍。下面通常Young GC的 GC日志看一下Young GC具體包括那些階段简识。JDK的采用的是HotSpot 1.8.0_241版本的JDK,JVM參數(shù)如下:
-XX:+UseG1GC -XX:G1HeapRegionSize=2m -Xms2g -Xmx2g -Xloggc:/Users/mac/Desktop/g1log -XX:+PrintGCDetails
-XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
2021-12-29T10:03:58.217-0800: 0.244: [GC pause (G1 Evacuation Pause) (young), 0.0914253 secs]
[Parallel Time: 90.3 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 244.0, Avg: 244.1, Max: 244.1, Diff: 0.1]
[Ext Root Scanning (ms): Min: 0.1, Avg: 0.3, Max: 0.7, Diff: 0.7, Sum: 2.2]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[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]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.2, Diff: 0.2, Sum: 0.2]
[Object Copy (ms): Min: 89.1, Avg: 89.6, Max: 89.8, Diff: 0.7, Sum: 716.5]
[Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8]
[Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 8]
[GC Worker Other (ms): Min: 0.1, Avg: 0.2, Max: 0.2, Diff: 0.1, Sum: 1.3]
[GC Worker Total (ms): Min: 90.1, Avg: 90.1, Max: 90.2, Diff: 0.1, Sum: 721.0]
[GC Worker End (ms): Min: 334.2, Avg: 334.2, Max: 334.2, Diff: 0.1]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 1.1 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.7 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 102.0M(102.0M)->0.0B(88.0M) Survivors: 0.0B->14.0M Heap: 102.0M(2048.0M)->98.0M(2048.0M)]
[Times: user=0.12 sys=0.28, real=0.09 secs]
如上GC日志第一行所示 GC pause (G1 Evacuation Pause) (young) 代表本次GC 暫停為G1 的 Young GC感猛。Young GC的工作分為并行工作與其他工作七扰,為別為GC日志中的 [Parallel Time: 90.3 ms, GC Workers: 8] 與 [Other: 1.1 ms]。并行工作是Young GC的主要工作內(nèi)容陪白,并行工作被拆分為如下幾部分颈走。
1、External Root Scanning(GC日志中的 [Ext Root Scanning (ms): ...部分)
負(fù)責(zé)處理掃描指向CSet的外部根咱士,例如寄存器立由、線程堆棧等轧钓。
2、Update Remembered Sets (RSets) (GC日志中的 [Update RS (ms): ...部分)
負(fù)責(zé)更新RSet锐膜,RSet之前詳細(xì)介紹過主要是用來記錄別的Region point-in 進(jìn)來的引用毕箍。
3、Processed Buffers(GC日志中的[Processed Buffers: ...部分)
前面說過RSet的維護(hù)是通過先寫log buffer然后再更新道盏,Processed Buffers 便是處理那些在Young GC開始后還沒有被Refinement Thread 處理完的 log buffer而柑,這保證的RSet的完整性。
4荷逞、Scan RSets(GC日志中的[Scan RS (ms): ...部分)
負(fù)責(zé)掃描RSets中其他Region指向本Region的引用媒咳。這個(gè)掃描時(shí)間會(huì)因?yàn)镽Set 的 Popularity 不同采用不同的粒度的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)而相差很多;前面介紹過的Coarse Grained時(shí)RSet的掃描時(shí)間將最耗時(shí)种远。
5涩澡、Code Root Scanning(GC日志中的[Code Root Scanning (ms): ...部分)
負(fù)責(zé)掃描CSet中已編譯源代碼的引用根。
6坠敷、Object Copy (GC日志中的[Object Copy (ms): ...部分)
負(fù)責(zé)將新生代Region筏养,即Eden Region與 Survivor Region中依賴存活的對(duì)象復(fù)制到未使用的Survivor Region中或者將晉升的對(duì)象復(fù)制到Old Region中。
7常拓、Termination (GC日志中的[Termination (ms): ...與 [Termination Attempts (ms): ...)部分
當(dāng)每個(gè)GC Work線程完成其自身的工作后渐溶,會(huì)進(jìn)行了結(jié)束階段,這時(shí)已完成工作的Work 線程會(huì)與其他Work線程同步弄抬,同時(shí)嘗試采用工作竊取算法獲取還未完成工作的其他線程的工作茎辐。Termination Attempts 部分代表Work線程成功獲取工作,處理完后再次嘗試結(jié)束掂恕。這個(gè)過程其還會(huì)再次嘗試獲取其他未完成工作線程的任務(wù)拖陆。
其他工作,這部分主要是一些他們的任務(wù)包括選擇Regino進(jìn)入CSet懊亡、引用處理依啰、引用入列隊(duì)、重新標(biāo)記卡頁為臟頁店枣、釋放CSet速警、處理Humongous對(duì)象等。關(guān)于G1 GC 日志更詳細(xì)的解釋可以參考Collecting and reading G1 garbage collector logs - part 2鸯两。
當(dāng)年老代內(nèi)存占比超過了-XX:InitiatingHeapOccupancyPercent指定的閾值闷旧,Young GC會(huì)順便完成并發(fā)標(biāo)記的初始化標(biāo)記工作。這時(shí)在GC日志中將出現(xiàn) GC pause (G1 Evacuation Pause) (young) (initial-mark) 的關(guān)鍵信息钧唐,其中的initial-mark 代表在進(jìn)行Young GC時(shí)稍帶完成的初始化標(biāo)記工作忙灼。
4.2 G1中的SATB具體實(shí)現(xiàn)
文章的最開始已介紹過垃圾收集器通常會(huì)采用Tri-color Marking 算法來處理標(biāo)記階段對(duì)象引用的分析過程,在處理漏標(biāo)問題上G1采用了Yuasa在Real-Time Garbage Collection on General Purpose Machines 提出的“snapshot-at-the-beginning” (SATB) 算法。
SATB 算法確保在并發(fā)標(biāo)記開始后所有的垃圾對(duì)象都通過快照被識(shí)別出來该园。在并發(fā)標(biāo)記過程中新分配的對(duì)象被認(rèn)為是存活的對(duì)象酸舍,不用對(duì)他們進(jìn)行追蹤分析,這有利于減小標(biāo)記的開銷里初。G1維護(hù)二個(gè)用于并發(fā)標(biāo)記的全局bitmap父腕,分別被標(biāo)記為previous與next。previous位圖中保存了前一次并發(fā)標(biāo)記的標(biāo)記信息青瀑,next 位圖保存了當(dāng)前正在進(jìn)行或剛完成并發(fā)標(biāo)記的標(biāo)記信息璧亮。previous bitmap中上次并發(fā)標(biāo)記的標(biāo)記信息,在本次本發(fā)標(biāo)記中可以直接使用斥难。同時(shí)每個(gè)Region都有幾個(gè)重要的指針 PTAMS(上一次并發(fā)標(biāo)記的起始位置)枝嘶、NTAMS(下一次并發(fā)標(biāo)記的起始位置)、Bottom(Region的起始地址)哑诊、Top(Region已使用地址)群扶、End(Region的結(jié)束地址);TAMS實(shí)現(xiàn)是 top at mark start的縮寫镀裤,也就是每次并發(fā)標(biāo)記會(huì)把對(duì)應(yīng)的指針放在Top針指同一位置竞阐,代表標(biāo)志的結(jié)束位置 。每次并發(fā)標(biāo)記開始時(shí)暑劝,NTAMS指針重新指Top指針的位置骆莹,當(dāng)并發(fā)標(biāo)記結(jié)束后,NTAMS指針與 PTAMS指針會(huì)交位置担猛,next bitmap 與 previous bitmap 交換角色幕垦,新的next bitmap 被清空,即原來的 previous bitmap被清空傅联。
[圖片上傳失敗...(image-6d2f30-1675209523360)]
上圖展示了某次并發(fā)標(biāo)記過程中一個(gè)Region中 Bottom先改、PTAMS、NTAMS蒸走、Top仇奶、End指針位置,指針之間區(qū)域的含義比驻。區(qū)間中的白色该溯、灰色、黑色可以大致理解為三色標(biāo)記法中的三種顏色嫁艇。
[Bottom, PTAMS) 區(qū)間
該區(qū)間代表上次并發(fā)標(biāo)記的區(qū)間朗伶,PTAMS為上次并發(fā)標(biāo)記的結(jié)束位置,該區(qū)間上次并發(fā)標(biāo)記的信息能直接被正在進(jìn)行的并發(fā)標(biāo)記利用步咪,即正在進(jìn)行的并發(fā)標(biāo)記通過上次bitmap知道該區(qū)間哪些是垃圾哪些是存活對(duì)象。
[PTAMS, NTAMS) 區(qū)間
該區(qū)間代表本次正在進(jìn)行的并發(fā)標(biāo)記的區(qū)間益楼,NTAMS為本次并發(fā)標(biāo)記的結(jié)束位置猾漫,在并發(fā)標(biāo)記開始時(shí)G1會(huì)為[PTAMS, NTAMS) 區(qū)間創(chuàng)建一個(gè)快照点晴,實(shí)際就是next bitmap,然后處理bitmap映射的地址悯周,標(biāo)記這些地址上的對(duì)象是垃圾還是存活的粒督。實(shí)際標(biāo)記過程就是有一個(gè)指針從PTAMS指針位置一直移到NTAMS指針位置。
[NTAMS, Top) 區(qū)間
該區(qū)間代表并發(fā)標(biāo)記過程中禽翼,應(yīng)用線程新生成的對(duì)象屠橄,前面已說過在并發(fā)標(biāo)記過程中新分配的對(duì)象被認(rèn)為是存活的對(duì)象,所以上圖中該區(qū)間全是黑色的闰挡。并發(fā)標(biāo)記剛開始時(shí)Top指針與NTAMS指針處于同一位置锐墙,當(dāng)應(yīng)用線程每生成一個(gè)新對(duì)象時(shí),Top指針就會(huì)相應(yīng)的向End指針的方向右移长酗。
[Top, End) 區(qū)間
該區(qū)間代表Region中還沒有使用的空間溪北。
很顯然GC線程只會(huì)去處理[PTAMS, NTAMS) 區(qū)間完成標(biāo)記工作,而應(yīng)用線程運(yùn)行則會(huì)對(duì)[Bottom, Top)區(qū)間有影響夺脾。應(yīng)用線程對(duì)[Bottom, Top)區(qū)間中[NTAMS, Top)區(qū)間的影響并不會(huì)影響GC線程的并發(fā)標(biāo)記工作之拨,因?yàn)樵摬糠謶?yīng)用線程新增的對(duì)象都認(rèn)為是存活的對(duì)象。應(yīng)用線程對(duì)[Bottom, Top)區(qū)間中[PTAMS, NTAMS)區(qū)間的影響可能會(huì)影響GC線程的并發(fā)標(biāo)記工作咧叭,G1通過前面介紹的pre-write barrier來確保標(biāo)記的正確性蚀乔,即如果應(yīng)用線程在[PTAMS, NTAMS)區(qū)間內(nèi)增加了黑色對(duì)象對(duì)白色對(duì)象的引用,pre-write barrier內(nèi)部處理時(shí)會(huì)將白色對(duì)象設(shè)置為灰色對(duì)象菲茬,使得該對(duì)象能再次被標(biāo)記不會(huì)產(chǎn)生漏標(biāo)乙墙。應(yīng)用線程對(duì)[Bottom, Top)區(qū)間中[Bottom, PTAMS)區(qū)間的影響可能會(huì)影響GC線程的并發(fā)標(biāo)記工作,具體G1是如何處理這個(gè)有待考證生均,猜測應(yīng)該也是利用write barrier這里的技術(shù)听想。
有了上面介紹,再看一下 Sun公司 G1的論文Garbage-First Garbage Collection 中 Initial Marking Pause/Concurrent Marking 小節(jié)中的這個(gè)圖應(yīng)該會(huì)清晰點(diǎn)马胧。
Initial Marking階段汉买,當(dāng)Region首次被標(biāo)記時(shí),PrevBitmap為空佩脊,NextBitmap中有[PrevTAMS, NextTAMS)區(qū)間的塊照蛙粘,并發(fā)標(biāo)記結(jié)束后將確定Bitmap中哪些是垃圾對(duì)象, PrevTAMS指針與Bottom指針位置相同威彰,NextTAMS指針與Top指針位置相同出牧。
Remark 階段,[PrevTAMS, NextTAMS)區(qū)間的存活對(duì)象與垃圾對(duì)象被標(biāo)記出來歇盼,NextBitmap發(fā)生改變其中黑色部分表示標(biāo)記出來的存活對(duì)象舔痕,白色部分為垃圾對(duì)象。同時(shí)由于應(yīng)用程序生成了新的對(duì)象,Top指針的位置從NextTAMS指針處向右移動(dòng)了伯复。
Cleanup/GC Pauses階段慨代,NextBitmap 與 PrevBitmap互換角色,同時(shí)NextTAMS指針與PrevTAMS指針互換位置啸如。
新一輪標(biāo)記Initial Marking階段,NextTAMS指針重新指向Top指針叮雳,PrevBitmap保證了上一次的標(biāo)記信息想暗,NextBitmap中有[PrevTAMS, NextTAMS)區(qū)間的塊照。
新一輪標(biāo)記Remark階段帘不,再重復(fù)上面B的事情说莫。
新一輪標(biāo)記Remark階段,Cleanup/GC Pauses階段厌均,再重復(fù)上面C的事情唬滑。
4.3 并發(fā)標(biāo)記階段
并發(fā)標(biāo)記階段主要是將Mix GC時(shí)要收集的垃圾對(duì)象先進(jìn)行標(biāo)記,然后根據(jù)Region能釋放的內(nèi)存空間做一下排序棺弊,同時(shí)其會(huì)在標(biāo)記的最后階段直接釋放那些沒有存活對(duì)象的Region晶密,并將這些Region加入到可用Region列表中。并發(fā)標(biāo)記階段可細(xì)分為Initial Mark模她、Root Region Scanning稻艰、Concurrent Marking、Remark侈净、Cleanup等等五個(gè)階段尊勿。
Initial Mark 階段
當(dāng)年老代內(nèi)存占比超過 -XX:InitiatingHeapOccupancyPercent指定的閾值時(shí)會(huì)觸發(fā)并發(fā)標(biāo)記,并發(fā)標(biāo)記的第一個(gè)階段為Initial Mark畜侦,該階段會(huì)STW元扔,其只掃描GCRoot直接引用的對(duì)象,由于Young GC時(shí)也要掃描GCRoot直接引用的對(duì)象旋膳,Young GC時(shí)會(huì)順便完成Initial Mark的工作澎语。GC日志通常會(huì)有GC pause (G1 Evacuation Pause) (young) (initial-mark) 的關(guān)鍵信息,其中的initial-mark 代表在進(jìn)行Young GC時(shí)順便完成的初始化標(biāo)記工作验懊。
Root Region Scanning 階段
實(shí)際掃描的是新生代Survivor Region引用的對(duì)象擅羞,該階段必須在下次GC暫停前完成,因?yàn)镠eap要掃描存活對(duì)象的話义图,Survivor Region引用的對(duì)象必須先被識(shí)別减俏。
Concurrent Marking 階段
并發(fā)標(biāo)記階段GC線程與應(yīng)用線程是并發(fā)的,同時(shí)可以通過-XX:ConcGCThreads指定并行GC線程數(shù)碱工。前面已介紹過G1采用pre-write barrier 解決并發(fā)標(biāo)記過種中因?yàn)閼?yīng)用線程更新了并發(fā)開始階段創(chuàng)建的對(duì)象圖的快照導(dǎo)致的漏標(biāo)問題娃承,每個(gè)線程奏夫。并發(fā)標(biāo)記階段會(huì)順帶完成每個(gè)Region對(duì)象的計(jì)數(shù)工作,方便后面統(tǒng)計(jì)哪些Region能回收更多的內(nèi)存草慧。
Remark 階段
該階段實(shí)際是標(biāo)記的最后階段桶蛔,其會(huì)SWT匙头,這個(gè)階段就負(fù)責(zé)把剩下的引用處理完漫谷,該階段會(huì)處理之前SATB write barrier記錄的尚未處理引用。但其與與CMS的remark有本質(zhì)的區(qū)別蹂析,即G1的Remark的暫停只需要掃描SATB buffer舔示,而CMS的remark需要重新掃描里全部的dirty card 外加整個(gè)根集合,而此時(shí)整個(gè)新生代都會(huì)被當(dāng)作根集合的一部分电抚,因而CMS remark有可能會(huì)非常慢惕稻。
Cleanup 階段
該階段會(huì)階段會(huì)STW,其主要工作是重置標(biāo)記狀態(tài)蝙叛,如前面介紹的NextBitmap 與 PrevBitmap互換角色俺祠,同時(shí)NextTAMS指針與PrevTAMS指針互換位置。同時(shí)若發(fā)現(xiàn)有Region沒有存活對(duì)象借帘,則會(huì)直接將Region清空并將Region加入到空閑Region列表中蜘渣。當(dāng)然統(tǒng)計(jì)每個(gè)Region能回收多少垃圾的統(tǒng)計(jì)工作也在這個(gè)階段完成,這樣后Mix GC對(duì)象轉(zhuǎn)移時(shí)便能快速地確定CSet肺然。
4.4 Mix GC階段
MixGC階段主要負(fù)責(zé)回收部分年老代與全部新生代的Region蔫缸,G1會(huì)根據(jù)設(shè)置的目標(biāo)暫停時(shí)間-XX:MaxGCPauseMillis將并發(fā)標(biāo)記階段標(biāo)記好的Region,按其可以釋放內(nèi)存空間大小际起,依次進(jìn)行回收拾碌,即一個(gè)MixGC 階段會(huì)包含多次的MixGC,當(dāng)G1發(fā)現(xiàn)釋放垃圾對(duì)象獲取的內(nèi)存空間過小時(shí)其將停止MixGC街望。MixGC時(shí)在GC日志中將出現(xiàn)GC pause (G1 Evacuation Pause) (mixed)的關(guān)鍵信息校翔。作為兜底策略,當(dāng)G1垃圾回收過程釋放的內(nèi)存不足于滿足應(yīng)用程序中新對(duì)象對(duì)內(nèi)存要求時(shí)灾前,G1會(huì)采用Full GC處理所有Region防症。
- G1的運(yùn)作過程
G1的GC操作可以分為三種:Young GC,并發(fā)標(biāo)記周期(Old GC)和Mixed GC豫柬。
2.1 G1 Young GC
與其他收集器的新生代gc類似告希,G1的Young GC也是采用標(biāo)記-復(fù)制-清除算法。G1的Young GC并不是說現(xiàn)有的Eden區(qū)放滿了就會(huì)馬上觸發(fā)烧给,G1會(huì)計(jì)算下現(xiàn)在Eden區(qū)回收大概要多久時(shí)間燕偶,如果回收時(shí)間遠(yuǎn)遠(yuǎn)小于參數(shù) -XX:MaxGCPauseMills 設(shè)定的值,那么增加年輕代的region础嫡,繼續(xù)給新對(duì)象存放指么,不會(huì)馬上做Young GC酝惧;直到下一次Eden區(qū)放滿,G1計(jì)算回收時(shí)間接近參數(shù) -XX:MaxGCPauseMills 設(shè)定的值伯诬,那么就會(huì)觸發(fā)Young GC
2.2 G1 并發(fā)標(biāo)記周期
G1收集器的并發(fā)標(biāo)記周期大致可劃分為以下四個(gè)步驟:
初始標(biāo)記(Initial Marking晚唇,需要使用STW):僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,并且修改TAMS指針的值盗似,讓下一階段用戶線程并發(fā)運(yùn)行時(shí)哩陕,能正確地在可用的Region中分配新對(duì)象。這個(gè)階段需要Stop the world停頓線程赫舒,但耗時(shí)很短悍及,而且是借用進(jìn)行Minor GC的時(shí)候同步完成的,所以G1收集器在這個(gè)階段實(shí)際并沒有額外的停頓接癌。
并發(fā)標(biāo)記(Concurrent Marking心赶,無需STW):從GC Root開始對(duì)堆中對(duì)象進(jìn)行可達(dá)性分析缨叫,遞歸掃描整個(gè)堆里的對(duì)象圖,找出要回收的對(duì)象咏闪,這階段耗時(shí)較長,但可與用戶程序并發(fā)執(zhí)行(這也是G1的效率較高的原因)据某。該階段不用stop the world,因此會(huì)使用三色標(biāo)記法以及SATB機(jī)制來保證標(biāo)記的正確性筷狼。
最終標(biāo)記(Final Marking,需要使用STW):對(duì)用戶線程做另一個(gè)短暫的暫停严拒,用于處理并發(fā)階段結(jié)束后仍遺留下來的最后那少量的SATB記錄裤唠。在該階段會(huì)處理在并發(fā)標(biāo)記階段被用戶修改過引用關(guān)系的對(duì)象格二,即SATB機(jī)制的二次標(biāo)記顶猜。
篩選回收(Live Data Counting and Evacuation纲菌,需要使用STW):負(fù)責(zé)更新Region的統(tǒng)計(jì)數(shù)據(jù)嚣潜,對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序庇麦,根據(jù)用戶所期望的停頓時(shí)間來制定回收計(jì)劃垮媒,可以自由選擇任意多個(gè)Region構(gòu)成回收集,然后把決定回收的那一部分Region的存活對(duì)象復(fù)制到空的Region中它抱,再清理掉整個(gè)舊Region的全部空間(這個(gè)是標(biāo)準(zhǔn)的垃圾收集算法中的標(biāo)記-復(fù)制算法)馁蒂。這里的操作涉及存活對(duì)象的移動(dòng),是必須暫停用戶線程的沮脖,而且是由多條收集器線程并行完成的。
從上述階段的描述可以看出,G1收集器除了并發(fā)標(biāo)記外胚膊,其余階段也是要完全暫停用戶線程的(stop the world),換言之,它并非純粹地追求低延遲,官方給它設(shè)定的目標(biāo)是在延遲可控的情況下獲得盡可能高的吞吐量乔妈。
2.3 G1 Mixed GC
Mixed GC是指目標(biāo)為收集整個(gè)新生代以及部分老年代的垃圾收集波材。目前只有G1收集器會(huì)有這種行為唯灵。成功完成并發(fā)標(biāo)記周期后, 若是老年代的堆占有率達(dá)到參數(shù)(-XX:InitiatingHeapOccupancyPercent)設(shè)定的值則觸發(fā)Mixed GC流程,回收所有的Young和部分Old(根據(jù)期望的GC停頓時(shí)間確定old區(qū)垃圾收集的優(yōu)先順序)以及大對(duì)象區(qū)叁巨,Mixed GC過程主要使用復(fù)制算法,把各個(gè)region中存活的對(duì)象拷貝到別的region里去,拷貝過程中如果發(fā)現(xiàn)沒有足夠的空region能夠承載拷貝對(duì)象就會(huì)觸發(fā)一次Full GC。
補(bǔ)充:在垃圾收集和處理過程中,還有幾種情況下會(huì)觸發(fā)Full GC:
(1)并發(fā)模式失效
G1啟動(dòng)并發(fā)標(biāo)記周期棵譬,但是在混合gc之前,老年代就被填滿了骆撇,這時(shí)候G1就會(huì)放棄標(biāo)記周期趾唱,改為執(zhí)行Full gc,對(duì)應(yīng)的gc日志為:[GC concurrent-mark-abort]
解決辦法:發(fā)生這種失敗意味著堆的大小應(yīng)該增加了征炼,或者G1收集器的后臺(tái)處理應(yīng)該更早開始雄右,或者需要調(diào)整周期纺讲,讓它運(yùn)行得更快(如增加后臺(tái)處理的線程數(shù))擂仍。
(2)晉升失敗
G1在進(jìn)行新生代gc時(shí),老年代沒有足夠的內(nèi)存提供給晉升對(duì)象熬甚,將會(huì)觸發(fā)Full gc逢渔。對(duì)應(yīng)的gc日志為:to-space exhausted。解決這種問題的方式是:
a. 增加 -XX:G1ReservePercent 選項(xiàng)的值(并相應(yīng)增加總的堆大邢缋ā)敷扫,為“目標(biāo)空間”增加預(yù)留內(nèi)存量膛腐。
b. 通過減少 -XX:InitiatingHeapOccupancyPercent 提前啟動(dòng)標(biāo)記周期沧踏。
c. 也可以通過增加 -XX:ConcGCThreads 選項(xiàng)的值來增加并行標(biāo)記線程的數(shù)目。
(3) 疏散失敗
進(jìn)行新生代gc時(shí),survivor和老年代沒有足夠的空間容納存活的對(duì)象智绸。對(duì)應(yīng)的gc日志為: to-space overflow。解決辦法與晉升失敗的情況是一樣的。
(4) 巨型對(duì)象分配失敗
巨型對(duì)象分配失敗也會(huì)觸發(fā)Full gc,解決辦法:增大regionSize糊渊。
(5)metaspace gc
metaspace大小達(dá)到閾值(metaspaceSize大小扳抽,是動(dòng)態(tài)的),會(huì)觸發(fā)Full gc缚忧。
G1的調(diào)優(yōu)參數(shù)
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定并行工作的GC線程數(shù)空执,也就是在STW階段工作的GC線程數(shù)。
-XX:ConcGCThreads:指定并發(fā)工作的GC線程數(shù)门坷,默認(rèn)是-XX:ParallelGCThreads的四分之一,也就是在非STW期間的GC工作線程數(shù)脯宿,當(dāng)然其他的線程很多工作在應(yīng)用上。當(dāng)并發(fā)周期時(shí)間過長時(shí),可以嘗試調(diào)大GC工作線程數(shù),但是這也意味著此期間應(yīng)用所占的線程數(shù)減少买决,會(huì)對(duì)吞吐量有一定影響谴古。
-XX:G1HeapRegionSize:指定分區(qū)大小(1MB~32MB,且必須是2的N次冪)憾筏,默認(rèn)將整堆劃分為2048個(gè)分區(qū)。Region的大小主要是關(guān)系到Humongous Object的判定,當(dāng)一個(gè)對(duì)象超過Region大小的一半時(shí)财边,則為巨型對(duì)象,那么其會(huì)至少獨(dú)占一個(gè)Region点骑,如果一個(gè)放不下酣难,會(huì)占用連續(xù)的多個(gè)Region。
-XX:MaxGCPauseMillis:目標(biāo)暫停時(shí)間(默認(rèn)200ms)黑滴,表示每次GC最大的停頓毫秒數(shù)憨募,默認(rèn)值是200ms,VM將調(diào)整Java堆大小和其他與GC相關(guān)的參數(shù)袁辈,以使GC引起的暫停時(shí)間短于預(yù)設(shè)值菜谣。這個(gè)值不能設(shè)置的過小,如果設(shè)置過小則每一次垃圾處理所能選擇的Region區(qū)域會(huì)減少晚缩,這會(huì)導(dǎo)致GC次數(shù)的增加尾膊,可能最后GC的垃圾清理速度趕不上應(yīng)用產(chǎn)生的速度,那么可能會(huì)造成串行的Full GC荞彼,這是要極力避免的冈敛。所以暫停時(shí)間肯定不是設(shè)置的越小越好,當(dāng)然也不能設(shè)置的偏大鸣皂,轉(zhuǎn)而指望G1自己會(huì)盡快的處理抓谴,這樣可能會(huì)導(dǎo)致一次全部并發(fā)標(biāo)記后觸發(fā)的Mixed GC次數(shù)變少暮蹂,但每次的時(shí)間變長,STW時(shí)間變長癌压,對(duì)應(yīng)用的影響更加明顯仰泻。
-XX:G1NewSizePercent和-XX:G1MaxNewSizePercent,分別為新生代比例的設(shè)定數(shù)值的下限和上限滩届,默認(rèn)值分別為5%和60%集侯。G1會(huì)根據(jù)實(shí)際的GC情況(主要是暫停時(shí)間)來動(dòng)態(tài)的調(diào)整新生代的大小,主要是調(diào)整Eden Region的個(gè)數(shù)丐吓。
-XX:TargetSurvivorRatio:Survivor區(qū)的填充容量(默認(rèn)50%)浅悉,Survivor區(qū)域里的一批對(duì)象(年齡1+年齡2+年齡n的多個(gè)年齡對(duì)象)總和超過了Survivor區(qū)域的50%,此時(shí)就會(huì)把年齡n(含)以上的對(duì)象都放入老年代券犁,默認(rèn)值是15术健。一般新生對(duì)象經(jīng)過15次Young GC會(huì)晉升到老年代,巨型對(duì)象會(huì)直接分配在老年代粘衬,同時(shí)在Young GC時(shí)荞估,如果相同age的對(duì)象占Survivors空間的比例超過 -XX:TargetSurvivorRatio的值(默認(rèn)50%),則會(huì)自動(dòng)將此次晉升年齡閾值設(shè)置為此age的值稚新,所有年齡超過此值的對(duì)象都會(huì)被晉升到老年代勘伺,此舉可能會(huì)導(dǎo)致老年代需要不少空間應(yīng)對(duì)此種晉升。一般這個(gè)值不需要額外調(diào)整褂删。
-XX:InitiatingHeapOccupancyPercent:老年代占用空間達(dá)到整堆內(nèi)存閾值(默認(rèn)45%)飞醉,如果Mixed GC周期結(jié)束后老年代使用率還是超過InitiatingHeapOccupancyPercent值,那么會(huì)再次觸發(fā)全局并發(fā)標(biāo)記過程,這樣就會(huì)導(dǎo)致頻繁的老年代GC屯阀,影響應(yīng)用吞吐量缅帘。同時(shí)老年代空間不大,Mixed GC回收的空間肯定是偏少的难衰∏瘴蓿可以適當(dāng)調(diào)高該值,當(dāng)然如果該值太高盖袭,很容易導(dǎo)致年輕代晉升失敗而出發(fā)Full GC失暂,所以需要多次調(diào)整測試。
-XX:G1HeapWastePercent(默認(rèn)5%): gc過程中空出來的region是否充足閾值鳄虱,在混合回收的時(shí)候弟塞,對(duì)Region回收都是基于復(fù)制算法進(jìn)行的,都是把要回收的Region里的存活對(duì)象放入其他Region拙已,然后這個(gè)Region中的垃圾對(duì)象全部清理掉宣肚,這樣的話在回收過程就會(huì)不斷空出來新的Region,一旦空閑出來的Region數(shù)量達(dá)到了堆內(nèi)存的5%悠栓,此時(shí)就會(huì)立即停止混合回收霉涨,意味著本次混合回收就結(jié)束了。
補(bǔ)充:雖然G1有不少優(yōu)秀的特性惭适,但是G1在垃圾收集時(shí)的內(nèi)存占用和程序額外負(fù)載都比CMS要高笙瑟,因此具體用什么垃圾收集器還是要從各方面考慮。一般來說癞志,小內(nèi)存應(yīng)用上往枷,CMS會(huì)比G1更占用;而在大內(nèi)存的服務(wù)器上(6G以上的內(nèi)存)凄杯,G1垃圾收集器能夠發(fā)揮出更大的優(yōu)勢错洁。