其他文章
【Java】深入理解Java虛擬機(jī)1——內(nèi)存區(qū)域以及OOM類型:http://www.reibang.com/p/65c91ba4006e
【Java】深入理解Java虛擬機(jī)2——判斷對(duì)象是否存活和引用:http://www.reibang.com/p/67c24aa93c03
【Java】深入理解Java虛擬機(jī)3——垃圾收集算法:http://www.reibang.com/p/362407886236
【Java】深入理解Java虛擬機(jī)4——內(nèi)存分配與回收策略:http://www.reibang.com/p/e21f5d5c4f42
【Java】深入理解Java虛擬機(jī)5——類的加載過(guò)程:http://www.reibang.com/p/931ef115d48e
【Java】深入理解Java虛擬機(jī)6——類的加載器及雙親委派:http://www.reibang.com/p/2f33eca93a4f
標(biāo)記-清除算法
最基礎(chǔ)的收集算法是“標(biāo)記-清除”(Mark-Sweep)算法仿吞,如同它的名字一樣,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象捡偏,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象唤冈。它的主要不足有兩個(gè):一個(gè)是效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高银伟;另一個(gè)是空間問(wèn)題你虹,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致以后在程序運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí)彤避,無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作傅物。
復(fù)制算法
為了解決效率問(wèn)題,一種稱為“復(fù)制”(Copying)的收集算法出現(xiàn)了琉预,它將可用內(nèi)存按容量劃分為大小相等的兩塊挟伙,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了模孩,就將還存活著的對(duì)象復(fù)制到另外一塊上面尖阔,然后再把已使用過(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)的一半数焊,未免太高了一點(diǎn)永淌。
現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法來(lái)回收新生代,是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空間佩耳,每次使用Eden和其中一塊Survivor遂蛀。當(dāng)回收時(shí),將Eden和Survivor中還存活著的對(duì)象一次性地復(fù)制到另外一塊Survivor空間上干厚,最后清理掉Eden和剛才用過(guò)的Survivor空間李滴。
標(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)存
分代收集算法
當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用“分代收集”(Generational Collection)算法酱讶,這種算法并沒(méi)有什么新的思想,只是根據(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)行回收惕医。具體的如何分代的,有哪些區(qū)域算色,回收策略是什么 抬伺?我會(huì)在后續(xù)的文章中整理。
理解GC日志
33.125:[GC[DefNew:3324K->152K(3712K)灾梦,0.0025925 secs]3324K->152K(11904K)峡钓,0.0031680 secs]
1 0 0.6 6 7:[Full GC[Tenured:0K->210K(10240K),0.0149142secs]4603K->210K(19456K)若河,
[Perm:2999K->2999K(21248K)]能岩,0.0150007 secs][Times:user=0.01 sys=0.00,real=0.02 secs]
1.最前面的數(shù)字“33.125:”和“100.667:”代表了GC發(fā)生的時(shí)間萧福,這個(gè)數(shù)字的含義是從Java虛擬機(jī)啟動(dòng)以來(lái)經(jīng)過(guò)的秒數(shù)拉鹃。
2.GC日志開(kāi)頭的“[GC”和“[Full GC”說(shuō)明了這次垃圾收集的停頓類型,而不是用來(lái)區(qū)分新生代GC還是老年代GC的统锤。
3.接下來(lái)的“[DefNew”毛俏、“[Tenured”、“[Perm”表示GC發(fā)生的區(qū)域饲窿,這里顯示的區(qū)域名稱與使用的GC收集器是密切相關(guān)的,
4.后面方括號(hào)內(nèi)部的“3324K->152K(3712K)”含義是“GC前該內(nèi)存區(qū)域已使用容量->GC后該內(nèi)存區(qū)域已使用容量(該內(nèi)存區(qū)域總?cè)萘浚被捞恪6诜嚼ㄌ?hào)之外的“3324K->152K(11904K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆總?cè)萘浚薄?br>
5.再往后逾雄,“0.0025925 secs”表示該內(nèi)存區(qū)域GC所占用的時(shí)間,單位是秒。
錯(cuò)誤不足之處或相關(guān)建議歡迎大家評(píng)論指出鸦泳,謝謝银锻!如果覺(jué)得內(nèi)容可以的話麻煩喜歡(?)一下。您的支持是我最大的動(dòng)力做鹰。