我們接著上面一篇繼續(xù)學(xué)習(xí)JVM的基本知識(shí)找颓。
對(duì)象存活判斷
上篇中我們介紹過(guò)JVM垃圾回收綜述中說(shuō)過(guò)一次垃圾回收之后會(huì)有一些對(duì)象存活谈截。這節(jié)我們介紹兩個(gè)判斷對(duì)象存活的算法嵌施。
判斷對(duì)象存活有引用計(jì)數(shù)算法和可達(dá)性分析算法腿箩。
1挣磨、引用計(jì)數(shù)算法
給每一個(gè)對(duì)象添加一個(gè)引用計(jì)數(shù)器雇逞,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值加1茁裙;每當(dāng)有一個(gè)地方不再引用它時(shí)塘砸,計(jì)數(shù)器值減1,這樣只要計(jì)數(shù)器的值不為0晤锥,就說(shuō)明還有地方引用它谣蠢,它就不是無(wú)用的對(duì)象。
這種方法看起來(lái)非常簡(jiǎn)單,但目前許多主流的虛擬機(jī)都沒(méi)有選用這種算法來(lái)管理內(nèi)存眉踱,原因就是當(dāng)某些對(duì)象之間互相引用時(shí)挤忙,無(wú)法判斷出這些對(duì)象是否已死。如下圖谈喳,對(duì)象1和對(duì)象2都沒(méi)有被堆外的變量引用册烈,而是被對(duì)方互相引用,這時(shí)他們雖然沒(méi)有用處了婿禽,但是引用計(jì)數(shù)器的值仍然是1赏僧,無(wú)法判斷他們是死對(duì)象,垃圾回收器也就無(wú)法回收扭倾。
2淀零、可達(dá)性分析算法
了解可達(dá)性分析算法之前先了解一個(gè)概念——GC Roots,垃圾收集的起點(diǎn)膛壹,可以作為GC Roots的有虛擬機(jī)棧中本地變量表中引用的對(duì)象驾中、方法區(qū)中靜態(tài)屬性引用的對(duì)象、方法區(qū)中常量引用的對(duì)象模聋、本地方法棧中JNI(Native方法)引用的對(duì)象肩民。 當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連(GC Roots到這個(gè)對(duì)象不可達(dá))時(shí),就說(shuō)明此對(duì)象是不可用的链方,是死對(duì)象持痰。如下圖:object1、object2祟蚀、object3工窍、object4和GC Roots之間有可達(dá)路徑,這些對(duì)象不會(huì)被回收前酿,但object5患雏、object6、object7到GC Roots之間沒(méi)有可達(dá)路徑薪者,這些對(duì)象就是死對(duì)象纵苛。
上面被判定為非存活的死對(duì)象(object5、object6言津、object7)并不是必死無(wú)疑攻人,還有挽救的余地。進(jìn)行可達(dá)性分析后對(duì)象和GC Roots之間沒(méi)有引用鏈相連時(shí)悬槽,對(duì)象將會(huì)被進(jìn)行一次標(biāo)記怀吻,接著會(huì)判斷如果對(duì)象沒(méi)有覆蓋Object的finalize()方法或者finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過(guò),那么它們就會(huì)清除初婆;如果對(duì)象覆蓋了finalize()方法且還沒(méi)有被調(diào)用蓬坡,則會(huì)執(zhí)行finalize()方法中的內(nèi)容猿棉,所以在finalize()方法中如果重新與GC Roots引用鏈上的對(duì)象關(guān)聯(lián)就可以拯救自己。當(dāng)然屑咳,實(shí)際中一般不會(huì)這么做萨赁。
GC算法
接下來(lái)講GC的算法,主要有標(biāo)記-清除算法兆龙、復(fù)制算法杖爽、標(biāo)記-整理算法、分代收集算法紫皇。
1慰安、標(biāo)記-清除算法
最基礎(chǔ)的收集算法是“標(biāo)記-清除”(Mark-Sweep)算法,分兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象聪铺,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象化焕。
優(yōu)點(diǎn):不需要進(jìn)行對(duì)象的移動(dòng),并且僅對(duì)不存活的對(duì)象進(jìn)行處理铃剔,在存活對(duì)象比較多的情況極為有效撒桨。
不足:一個(gè)是效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高番宁;另一個(gè)是空間問(wèn)題元莫,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片赖阻,空間碎片太多可能導(dǎo)致以后在程序運(yùn)行過(guò)程需要分配較大對(duì)象時(shí)蝶押,無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一個(gè)的垃圾收集動(dòng)作。
下面兩張圖從兩個(gè)角度闡明了標(biāo)記-清楚算法:
2火欧、復(fù)制算法
為了解決效率問(wèn)題棋电,一種稱為復(fù)制(Copying)的收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊苇侵,每次只使用其中的一塊赶盔。當(dāng)這一塊內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上榆浓,然后再把已經(jīng)使用過(guò)的內(nèi)存空間一次清理掉于未。這樣使得每次都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況陡鹃,只要移動(dòng)堆頂指針烘浦,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單萍鲸,運(yùn)行高效闷叉。代價(jià)是內(nèi)存縮小為原來(lái)的一半。
復(fù)制算法過(guò)程如下面兩張圖表示:
商業(yè)虛擬機(jī)用這個(gè)回收算法來(lái)回收新生代脊阴。IBM研究表明98%的對(duì)象是“朝生夕死“握侧,不需要按照1-1的比例來(lái)劃分內(nèi)存空間蚯瞧,而是將內(nèi)存分為一塊較大的”Eden“空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor品擎。當(dāng)回收時(shí)埋合,將Eden和Survivor中還存活的對(duì)象一次性復(fù)制到另外一個(gè)Survivor空間上,最后清理掉Eden和剛才用過(guò)的Survivor空間萄传。Hotspot虛擬機(jī)默認(rèn)Eden和Survivor的比例是8-1.即每次可用整個(gè)新生代的90%, 只有一個(gè)survivor饥悴,即1/10被”浪費(fèi)“。當(dāng)然盲再,98%的對(duì)象回收只是一般場(chǎng)景下的數(shù)據(jù)西设,我們沒(méi)有辦法保證每次回收都只有不多于10%的對(duì)象存活,當(dāng)Survivor空間不夠時(shí)答朋,需要依賴其他內(nèi)存(老年代)進(jìn)行分配擔(dān)保(Handle Promotion).
如果另外一塊survivor空間沒(méi)有足夠空間存放上一次新生代收集下來(lái)的存活對(duì)象時(shí)贷揽,這些對(duì)象將直接通過(guò)分配擔(dān)保機(jī)制進(jìn)入老年代。
下面大概介紹一下這個(gè)eden survivor復(fù)制的過(guò)程梦碗。
Eden Space字面意思是伊甸園禽绪,對(duì)象被創(chuàng)建的時(shí)候首先放到這個(gè)區(qū)域,進(jìn)行垃圾回收后洪规,不能被回收的對(duì)象被放入到空的survivor區(qū)域印屁。
Survivor Space幸存者區(qū),用于保存在eden space內(nèi)存區(qū)域中經(jīng)過(guò)垃圾回收后沒(méi)有被回收的對(duì)象斩例。Survivor有兩個(gè)雄人,分別為T(mén)o Survivor、 From Survivor念赶,這個(gè)兩個(gè)區(qū)域的空間大小是一樣的础钠。執(zhí)行垃圾回收的時(shí)候Eden區(qū)域不能被回收的對(duì)象被放入到空的survivor(也就是To Survivor,同時(shí)Eden區(qū)域的內(nèi)存會(huì)在垃圾回收的過(guò)程中全部釋放)叉谜,另一個(gè)survivor(即From Survivor)里不能被回收的對(duì)象也會(huì)被放入這個(gè)survivor(即To Survivor)旗吁,然后To Survivor 和 From Survivor的標(biāo)記會(huì)互換,始終保證一個(gè)survivor是空的停局。
為啥需要兩個(gè)survivor很钓?因?yàn)樾枰粋€(gè)完整的空間來(lái)復(fù)制過(guò)來(lái)。當(dāng)滿的時(shí)候晉升董栽。每次都往標(biāo)記為to的里面放码倦,然后互換,這時(shí)from已經(jīng)被清空裆泳,可以當(dāng)作to了叹洲。
3、標(biāo)記-整理算法
復(fù)制收集算法在對(duì)象成活率較高時(shí)就要進(jìn)行較多的復(fù)制操作工禾,效率將會(huì)變低运提。更關(guān)鍵的是蝗柔,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保民泵,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都100%存活的極端情況癣丧,所以,老年代一般不能直接選用這種算法栈妆。
根據(jù)老年代的特點(diǎn)胁编,有人提出一種”標(biāo)記-整理“Mark-Compact算法,標(biāo)記過(guò)程仍然和標(biāo)記-清除一樣鳞尔,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理嬉橙,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理端邊界以外的內(nèi)存寥假。
下面兩張圖講了這個(gè)算法的過(guò)程:
4市框、分代收集算法
當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用”分代收集“(Generational Collection)算法,這種算法根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊糕韧。一般把Java堆分為新生代和老年代枫振,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴āT谛律┎剩看卫占瘯r(shí)都發(fā)現(xiàn)大批對(duì)象死去粪滤,只有少量存活,那就選用復(fù)制算法雀扶,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集杖小。而老年代中因?yàn)閷?duì)象存活率較高,沒(méi)有額外的空間對(duì)它進(jìn)行分配擔(dān)保怕吴,就必須使用”標(biāo)記-清理“和”標(biāo)記-整理“算法來(lái)進(jìn)行回收窍侧。
這種算法就是我們?cè)谇懊鍶VM垃圾回收綜述中講述的內(nèi)容县踢。其本質(zhì)是更為靈活的使用”標(biāo)記-清理“和”標(biāo)記-整理“算法转绷。
常見(jiàn)的GC回收器
現(xiàn)在常見(jiàn)的垃圾收集器有如下幾種
新生代收集器:Serial、ParNew硼啤、Parallel Scavenge
老年代收集器:Serial Old议经、CMS、Parallel Old
堆內(nèi)存垃圾收集器:G1
如圖所示:
0谴返、垃圾收集時(shí)間
當(dāng)程序運(yùn)行時(shí)煞肾,各種數(shù)據(jù)、對(duì)象嗓袱、線程籍救、內(nèi)存等都時(shí)刻在發(fā)生變化,當(dāng)下達(dá)垃圾收集命令后垃圾收集器并不會(huì)立刻執(zhí)行垃圾收集渠抹。為了搞明白垃圾收集器的工作原理蝙昙,我們需要講兩個(gè)名詞:安全點(diǎn)(safepoint)和安全區(qū)(safe region)闪萄。
安全點(diǎn):從線程角度看,安全點(diǎn)可以理解為是在代碼執(zhí)行過(guò)程中的一些特殊位置奇颠,當(dāng)線程執(zhí)行到安全點(diǎn)的時(shí)候败去,說(shuō)明虛擬機(jī)當(dāng)前的狀態(tài)是安全的,如果有需要烈拒,可以在這里暫停用戶線程圆裕。當(dāng)垃圾收集時(shí),如果需要暫停當(dāng)前的用戶線程荆几,但用戶線程當(dāng)時(shí)沒(méi)在安全點(diǎn)上吓妆,則應(yīng)該等待這些線程執(zhí)行到安全點(diǎn)再暫停。
安全區(qū):安全點(diǎn)是相對(duì)于運(yùn)行中的線程來(lái)說(shuō)的吨铸,對(duì)于如sleep或blocked等狀態(tài)的線程耿战,收集器不會(huì)等待這些線程被分配CPU時(shí)間,這時(shí)候只要線程處于安全區(qū)中焊傅,就可以算是安全的剂陡。安全區(qū)就是在一段代碼片段中,引用關(guān)系不會(huì)發(fā)生變化狐胎,可以看作是被擴(kuò)展鸭栖、拉長(zhǎng)了的安全點(diǎn)。
GC過(guò)程一定會(huì)發(fā)生STW(Stop The World)握巢,而一旦發(fā)生STW必然會(huì)影響用戶使用晕鹊,所以GC的發(fā)展都是在圍繞減少STW時(shí)間這一目的。
1暴浦、Serial 收集器
Serial是一款用于新生代的單線程收集器溅话,采用復(fù)制算法進(jìn)行垃圾收集。Serial進(jìn)行垃圾收集時(shí)歌焦,不僅只用一條線程執(zhí)行垃圾收集工作飞几,它在收集的同時(shí),所有的用戶線程必須暫停(Stop The World)独撇。 如下是Serial收集器和Serial Old收集器結(jié)合進(jìn)行垃圾收集的示意圖屑墨,當(dāng)用戶線程都執(zhí)行到安全點(diǎn)時(shí),所有線程暫停執(zhí)行纷铣,Serial收集器以單線程卵史,采用復(fù)制算法進(jìn)行垃圾收集工作,收集完之后搜立,用戶線程繼續(xù)開(kāi)始執(zhí)行以躯。
適用場(chǎng)景:Client模式(桌面應(yīng)用);單核服務(wù)器啄踊∮巧瑁可以用-XX:+UserSerialGC來(lái)選擇Serial作為新生代收集器色鸳。
2、ParNew 收集器
ParNew就是一個(gè)Serial的多線程版本见转,其它與Serial并無(wú)區(qū)別命雀。ParNew在單核CPU環(huán)境并不會(huì)比Serial收集器達(dá)到更好的效果,它默認(rèn)開(kāi)啟的收集線程數(shù)和CPU數(shù)量一致斩箫,可以通過(guò)-XX:ParallelGCThreads來(lái)設(shè)置垃圾收集的線程數(shù)吏砂。 如下是ParNew收集器和Serial Old收集器結(jié)合進(jìn)行垃圾收集的示意圖,當(dāng)用戶線程都執(zhí)行到安全點(diǎn)時(shí)乘客,所有線程暫停執(zhí)行狐血,ParNew收集器以多線程,采用復(fù)制算法進(jìn)行垃圾收集工作易核,收集完之后匈织,用戶線程繼續(xù)開(kāi)始執(zhí)行。
適用場(chǎng)景:多核服務(wù)器牡直;與CMS收集器搭配使用缀匕。當(dāng)使用-XX:+UserConcMarkSweepGC來(lái)選擇CMS作為老年代收集器時(shí),新生代收集器默認(rèn)就是ParNew碰逸,也可以用-XX:+UseParNewGC來(lái)指定使用ParNew作為新生代收集器乡小。
3、Parallel Scavenge 收集器
Parallel Scavenge也是一款用于新生代的多線程收集器饵史,與ParNew的不同之處是满钟,ParNew的目標(biāo)是盡可能縮短垃圾收集時(shí)用戶線程的停頓時(shí)間,Parallel Scavenge的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量胳喷。吞吐量就是CPU執(zhí)行用戶線程的的時(shí)間與CPU執(zhí)行總時(shí)間的比值【吞吐量=運(yùn)行用戶代代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間)】湃番,比如虛擬機(jī)一共運(yùn)行了100分鐘,其中垃圾收集花費(fèi)了1分鐘吭露,那吞吐量就是99% 吠撮。比如下面兩個(gè)場(chǎng)景,垃圾收集器每100秒收集一次奴饮,每次停頓10秒纬向,和垃圾收集器每50秒收集一次,每次停頓時(shí)間7秒戴卜,雖然后者每次停頓時(shí)間變短了,但是總體吞吐量變低了琢岩,CPU總體利用率變低了投剥。
收集頻率 | 每次停頓時(shí)間 | 吞吐量 |
---|---|---|
每100秒收集一次 | 10秒 | 91% |
每50秒收集一次 | 7秒 | 88% |
可以通過(guò)-XX:MaxGCPauseMillis來(lái)設(shè)置收集器盡可能在多長(zhǎng)時(shí)間內(nèi)完成內(nèi)存回收,可以通過(guò)-XX:GCTimeRatio來(lái)精確控制吞吐量担孔。
如下是Parallel收集器和Parallel Old收集器結(jié)合進(jìn)行垃圾收集的示意圖江锨,在新生代吃警,當(dāng)用戶線程都執(zhí)行到安全點(diǎn)時(shí),所有線程暫停執(zhí)行啄育,ParNew收集器以多線程酌心,采用復(fù)制算法進(jìn)行垃圾收集工作,收集完之后挑豌,用戶線程繼續(xù)開(kāi)始執(zhí)行安券;在老年代,當(dāng)用戶線程都執(zhí)行到安全點(diǎn)時(shí)氓英,所有線程暫停執(zhí)行侯勉,Parallel Old收集器以多線程,采用標(biāo)記整理算法進(jìn)行垃圾收集工作铝阐。
適用場(chǎng)景:注重吞吐量址貌,高效利用CPU,需要高效運(yùn)算且不需要太多交互徘键×范裕可以使用-XX:+UseParallelGC來(lái)選擇Parallel Scavenge作為新生代收集器,jdk7吹害、jdk8默認(rèn)使用Parallel Scavenge作為新生代收集器锹淌。
4、Serial Old收集器
Serial Old收集器是Serial的老年代版本赠制,同樣是一個(gè)單線程收集器赂摆,采用標(biāo)記-整理算法。
如下圖是Serial收集器和Serial Old收集器結(jié)合進(jìn)行垃圾收集的示意圖:
適用場(chǎng)景:Client模式(桌面應(yīng)用)钟些;單核服務(wù)器烟号;與Parallel Scavenge收集器搭配;作為CMS收集器的后備預(yù)案政恍。
5汪拥、CMS(Concurrent Mark Sweep) 收集器
CMS收集器是一種以最短回收停頓時(shí)間為目標(biāo)的收集器,以“最短用戶線程停頓時(shí)間”著稱篙耗。整個(gè)垃圾收集過(guò)程分為4個(gè)步驟:
初始標(biāo)記:標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象迫筑,速度較快
并發(fā)標(biāo)記:進(jìn)行GC Roots Tracing,標(biāo)記出全部的垃圾對(duì)象宗弯,耗時(shí)較長(zhǎng)
重新標(biāo)記:修正并發(fā)標(biāo)記階段引用戶程序繼續(xù)運(yùn)行而導(dǎo)致變化的對(duì)象的標(biāo)記記錄脯燃,耗時(shí)較短
并發(fā)清除:用標(biāo)記-清除算法清除垃圾對(duì)象,耗時(shí)較長(zhǎng)
整個(gè)過(guò)程耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除都是和用戶線程一起工作蒙保,所以從總體上來(lái)說(shuō)辕棚,CMS收集器垃圾收集可以看做是和用戶線程并發(fā)執(zhí)行的。
CMS收集器也存在一些缺點(diǎn):
對(duì)CPU資源敏感:默認(rèn)分配的垃圾收集線程數(shù)為(CPU數(shù)+3)/4,隨著CPU數(shù)量下降逝嚎,占用CPU資源越多扁瓢,吞吐量越小
無(wú)法處理浮動(dòng)垃圾:在并發(fā)清理階段,由于用戶線程還在運(yùn)行补君,還會(huì)不斷產(chǎn)生新的垃圾引几,CMS收集器無(wú)法在當(dāng)次收集中清除這部分垃圾。同時(shí)由于在垃圾收集階段用戶線程也在并發(fā)執(zhí)行挽铁,CMS收集器不能像其他收集器那樣等老年代被填滿時(shí)再進(jìn)行收集伟桅,需要預(yù)留一部分空間提供用戶線程運(yùn)行使用。當(dāng)CMS運(yùn)行時(shí)屿储,預(yù)留的內(nèi)存空間無(wú)法滿足用戶線程的需要贿讹,就會(huì)出現(xiàn)“Concurrent Mode Failure”的錯(cuò)誤,這時(shí)將會(huì)啟動(dòng)后備預(yù)案够掠,臨時(shí)用Serial Old來(lái)重新進(jìn)行老年代的垃圾收集民褂。
因?yàn)镃MS是基于標(biāo)記-清除算法,所以垃圾回收后會(huì)產(chǎn)生空間碎片疯潭,可以通過(guò)-XX:UserCMSCompactAtFullCollection開(kāi)啟碎片整理(默認(rèn)開(kāi)啟)赊堪,在CMS進(jìn)行Full GC之前,會(huì)進(jìn)行內(nèi)存碎片的整理竖哩。還可以用-XX:CMSFullGCsBeforeCompaction設(shè)置執(zhí)行多少次不壓縮(不進(jìn)行碎片整理)的Full GC之后哭廉,跟著來(lái)一次帶壓縮(碎片整理)的Full GC。
適用場(chǎng)景:重視服務(wù)器響應(yīng)速度相叁,要求系統(tǒng)停頓時(shí)間最短遵绰。可以使用-XX:+UserConMarkSweepGC來(lái)選擇CMS作為老年代收集器增淹。
6椿访、Parallel Old 收集器
Parallel Old收集器是Parallel Scavenge的老年代版本,是一個(gè)多線程收集器虑润,采用標(biāo)記-整理算法成玫。可以與Parallel Scavenge收集器搭配拳喻,可以充分利用多核CPU的計(jì)算能力哭当。如Parallel Scavenge中的兩個(gè)垃圾收集器的搭配使用圖。
適用場(chǎng)景:與Parallel Scavenge收集器搭配使用冗澈;注重吞吐量钦勘。jdk7、jdk8默認(rèn)使用該收集器作為老年代收集器渗柿,使用 -XX:+UseParallelOldGC來(lái)指定使用Paralle Old收集器个盆。
7脖岛、G1 收集器
上述的一些GC收集器通過(guò)并行與并發(fā)已經(jīng)極大的減少了STW的時(shí)間朵栖,但是STW的時(shí)間還是會(huì)因?yàn)楦鞣N原因不可控颊亮,而G1提供的一個(gè)最大功能就是可控的STW時(shí)間。
G1通過(guò)把Java堆分成大小相等的多個(gè)獨(dú)立區(qū)域陨溅,回收時(shí)計(jì)算出每個(gè)區(qū)域回收所獲得的空間以及所需時(shí)間的經(jīng)驗(yàn)值终惑,根據(jù)記錄兩個(gè)值來(lái)判斷哪個(gè)區(qū)域最具有回收價(jià)值,所以叫Garbage First(垃圾優(yōu)先)门扇。
這里有幾個(gè)重要的概念:
Region(區(qū)域):G1采用了分區(qū)(Region)的思路雹有,將整個(gè)堆空間分成若干個(gè)大小相等的內(nèi)存區(qū)域,每次分配對(duì)象空間將逐段地使用內(nèi)存臼寄。因此霸奕,在堆的使用上,G1并不要求對(duì)象的存儲(chǔ)一定是物理上連續(xù)的吉拳,只要邏輯上連續(xù)即可质帅;每個(gè)分區(qū)也不會(huì)確定地為某個(gè)代服務(wù),可以按需在年輕代和老年代之間切換留攒。啟動(dòng)時(shí)可以通過(guò)參數(shù)
-XX:G1HeapRegionSize=n
可指定分區(qū)大小(1MB~32MB煤惩,且必須是2的冪),默認(rèn)將整堆劃分為2048個(gè)分區(qū)炼邀。Card(卡片):在每個(gè)分區(qū)內(nèi)部又被分成了若干個(gè)大小為512 Byte卡片(Card)魄揉,標(biāo)識(shí)堆內(nèi)存最小可用粒度所有分區(qū)的卡片將會(huì)記錄在全局卡片表(Global Card Table)中,分配的對(duì)象會(huì)占用物理上連續(xù)的若干個(gè)卡片拭宁,當(dāng)查找對(duì)分區(qū)內(nèi)對(duì)象的引用時(shí)便可通過(guò)記錄卡片來(lái)查找該引用對(duì)象(見(jiàn)RSet)洛退。每次對(duì)內(nèi)存的回收,都是對(duì)指定分區(qū)的卡片進(jìn)行處理杰标。
CSet(收集集合):GC過(guò)程記錄的可被回收的Region的集合兵怯。在CSet中存活的數(shù)據(jù)會(huì)在GC過(guò)程中被移動(dòng)到另一個(gè)可用分區(qū),CSet中的分區(qū)可以來(lái)自eden空間在旱、survivor空間摇零、或者老年代。
RSet(Remembered Set 記憶集合):記錄了其他Region中的對(duì)象引用本Region中對(duì)象的關(guān)系桶蝎,屬于points-into結(jié)構(gòu) (誰(shuí)引用了我的對(duì)象)驻仅。作用是不需要掃描整個(gè)堆找到誰(shuí)引用了當(dāng)前分區(qū)中的對(duì)象,只需要掃描RSet即可登渣。
Humongous regions:用來(lái)存放大于標(biāo)準(zhǔn)的Region內(nèi)存50%的大對(duì)象區(qū)域噪服,如果有些對(duì)象大于整個(gè)Region就會(huì)去找連續(xù)的Region保存,如果沒(méi)有就會(huì)觸發(fā)GC胜茧。
G1收集器與之前的收集器最大的不同就在于堆內(nèi)存的劃分粘优,之前的收集器只區(qū)分新生代與老年代仇味,而G1收集器則是把堆內(nèi)存劃分成多個(gè)獨(dú)立的Region。
在上圖中G1的Java堆中每個(gè)Region都有一個(gè)身份雹顺,每個(gè)Region有可能是eden丹墨、survivor、old嬉愧,但是他們的身份僅僅是邏輯上的贩挣,是可以變化的,G1可以根據(jù)情況動(dòng)態(tài)的調(diào)整各種Region的數(shù)量没酣,通過(guò)控制回收的Region數(shù)量來(lái)控制STW的時(shí)間王财,以達(dá)到STW時(shí)間的可控制。
雖然G1收集器把Java堆化整為零成一個(gè)個(gè)Region裕便,但是也不會(huì)進(jìn)行所有Region進(jìn)行收集绒净,G1也分成了兩種收集模式,兩種模式如下:
Young GC: CSet就是所有年輕代里面的Region偿衰;
Mixed GC: CSet是所有年輕代里的Region加上在全局并發(fā)標(biāo)記階段標(biāo)記出來(lái)的收益高的老年代Region挂疆;
Young GC過(guò)程:
階段1:根掃描,靜態(tài)和本地對(duì)象被掃描哎垦;
階段2:更新RS囱嫩,處理dirty card隊(duì)列更新RS;
階段3:處理RS漏设,檢測(cè)從年輕代指向老年代的對(duì)象墨闲;
階段4:對(duì)象拷貝,拷貝存活的對(duì)象到survivorl/old區(qū)域郑口;
階段5:處理引用隊(duì)列鸳碧,軟引用,弱引用犬性,虛引用處理瞻离;
Mixed GC過(guò)程:
1、全局并發(fā)標(biāo)記(global concurrent marking)
2乒裆、拷貝存活對(duì)象(evacuation)
全局并發(fā)標(biāo)記包括5個(gè)步驟:
1套利、初始標(biāo)記(initial mark,STW):標(biāo)記了從GCRoot開(kāi)始直接可達(dá)的對(duì)象鹤耍。
2肉迫、根區(qū)域掃描(root region scan):G1 GC 在初始標(biāo)記的存活區(qū)掃描對(duì)老年代的引用,并標(biāo)記被引用的對(duì)象稿黄。該階段與應(yīng)用程序(非 STW)同時(shí)運(yùn)行喊衫,并且只有完成該階段后,才能開(kāi)始下一次 STW 年輕代垃圾回收杆怕。
3族购、并發(fā)標(biāo)記(Concurrent Marking):G1 GC 在整個(gè)堆中查找可訪問(wèn)的(存活的)對(duì)象壳贪。該階段與應(yīng)用程序同時(shí)運(yùn)行,可以被 STW 年輕代垃圾回收中斷寝杖。
4违施、重新標(biāo)記(Remark,STW):該階段是 STW 回收朝墩,幫助完成標(biāo)記周期醉拓。G1 GC 清空 SATB 緩沖區(qū)伟姐,跟蹤未被訪問(wèn)的存活對(duì)象收苏,并執(zhí)行引用處理。
5愤兵、清除垃圾(Cleanup):在這個(gè)最后階段鹿霸,G1 GC 執(zhí)行統(tǒng)計(jì)和 RSet 凈化的 STW 操作。在統(tǒng)計(jì)期間秆乳,G1 GC 會(huì)識(shí)別完全空閑的區(qū)域和可供進(jìn)行混合垃圾回收的區(qū)域懦鼠。清理階段在將空白區(qū)域重置并返回到空閑列表時(shí)為部分并發(fā)。
適用場(chǎng)景:要求盡可能可控GC停頓時(shí)間屹堰;內(nèi)存占用較大的應(yīng)用肛冶。可以用-XX:+UseG1GC使用G1收集器扯键,jdk9默認(rèn)使用G1收集器睦袖。
GC日志
每一種回收器的日志格式都是由其自身的實(shí)現(xiàn)決定的谚殊,換而言之谤专,每種回收器的日志格式都可以不一樣础拨。但虛擬機(jī)設(shè)計(jì)者為了方便用戶閱讀华畏,將各個(gè)回收器的日志都維持一定的共性杠步。
GC日志是學(xué)GC調(diào)優(yōu)之前的必備前置條件伪嫁,所以我們必須學(xué)會(huì)枫慷。下面放兩張網(wǎng)圖周拐,大家可以從中看到日志的每個(gè)節(jié)點(diǎn):
young gc 日志:
Full GC日志:
文章首發(fā)于:
九神帶你入門(mén)JVM(下)