一、常用垃圾回收機(jī)制
1. 標(biāo)記-清除算法(mark-sweep)
顧名思義痘括,標(biāo)記-清除算法分為兩個階段长窄,標(biāo)記(mark)和清除(sweep).
在標(biāo)記階段滔吠,collector從mutator根對象開始進(jìn)行遍歷,對從mutator根對象可以訪問到的對象都打上一個標(biāo)識挠日,一般是在對象的header中疮绷,將其記錄為可達(dá)對象。
而在清除階段嚣潜,collector對堆內(nèi)存(heap memory)從頭到尾進(jìn)行線性的遍歷冬骚,如果發(fā)現(xiàn)某個對象沒有標(biāo)記為可達(dá)對象-通過讀取對象的header信息,則就將其回收懂算。
[圖片上傳失敗...(image-8c300b-1510574775013)]
從上圖我們可以看到只冻,在Mark階段,從根對象1可以訪問到B對象犯犁,從B對象又可以訪問到E對象属愤,所以B,E對象都是可達(dá)的。同理酸役,F(xiàn),G,J,K也都是可達(dá)對象住诸。到了Sweep階段,所有非可達(dá)對象都會被collector回收涣澡。同時贱呐,Collector在進(jìn)行標(biāo)記和清除階段時會將整個應(yīng)用程序暫停(mutator),等待標(biāo)記清除結(jié)束后才會恢復(fù)應(yīng)用程序的運(yùn)行入桂。
缺點:
? 標(biāo)記-清除算法的比較大的缺點就是垃圾收集后有可能會造成大量的內(nèi)存碎片奄薇,像上面的圖片所示,垃圾收集后內(nèi)存中存在三個內(nèi)存碎片抗愁,假設(shè)一個方格代表1個單位的內(nèi)存馁蒂,如果有一個對象需要占用3個內(nèi)存單位的話,那么就會導(dǎo)致Mutator一直處于暫停狀態(tài)蜘腌,而Collector一直在嘗試進(jìn)行垃圾收集沫屡,直到Out of Memory。
2. 標(biāo)記-壓縮算法(mark-compact)
? 顧名思義撮珠,標(biāo)記-壓縮算法分為兩個階段沮脖,標(biāo)記(mark)和壓縮(compact).
? 其中標(biāo)記階段跟標(biāo)記-清除算法中的標(biāo)記階段是一樣的,而對于壓縮階段芯急,它的工作就是移動所有的可達(dá)對象到堆內(nèi)存的同一個區(qū)域中勺届,使他們緊湊的排列在一起,從而將所有非可達(dá)對象釋放出來的空閑內(nèi)存都集中在一起娶耍,通過這樣的方式來達(dá)到減少內(nèi)存碎片的目的免姿。
3. 復(fù)制算法(copying)
堆內(nèi)存對半分為兩個半?yún)^(qū),只用其中一個半?yún)^(qū)來進(jìn)行對象內(nèi)存的分配伺绽,如果在這個半?yún)^(qū)內(nèi)存不夠給新的對象分配了养泡,那么就開始進(jìn)行垃圾收集嗜湃,將這個半?yún)^(qū)中的所有可達(dá)對象都拷貝到另外一個半?yún)^(qū)中去,然后繼續(xù)在另外那個半?yún)^(qū)進(jìn)行新對象的內(nèi)存分配澜掩。
缺點:
? 存壓縮為原來的一半购披,利用率比較低,典型的空間換時間
4. 引用計數(shù)算法(reference counting)
? 通過在對象頭中分配一個空間來保存該對象被引用的次數(shù)肩榕。如果該對象被其它對象引用刚陡,則它的引用計數(shù)加一,如果刪除對該對象的引用株汉,那么它的引用計數(shù)就減一筐乳,當(dāng)該對象的引用計數(shù)為0時,那么該對象就會被回收乔妈。
? 采用引用計數(shù)的垃圾收集機(jī)制跟前面三種垃圾收集機(jī)制最大的不同在于蝙云,垃圾收集的開銷被分?jǐn)偟秸麄€應(yīng)用程序的運(yùn)行當(dāng)中了,而不是在進(jìn)行垃圾收集時路召,要掛起整個應(yīng)用的運(yùn)行勃刨,直到對堆中所有對象的處理都結(jié)束。因此股淡,采用引用計數(shù)的垃圾收集不屬于嚴(yán)格意義上的"Stop-The-World"的垃圾收集機(jī)制身隐。
注意:
當(dāng)某個對象的引用計數(shù)減為0時,collector需要遞歸遍歷它所指向的所有域唯灵,將它所有域所指向的對象的引用計數(shù)都減一贾铝,然后才能回收當(dāng)前對象。
-
但是這種引用計數(shù)算法有一個比較大的問題埠帕,那就是它不能處理環(huán)形數(shù)據(jù) - 即如果有兩個對象相互引用垢揩,那么這兩個對象就不能被回收,因為它們的引用計數(shù)始終為1敛瓷。這也就是我們常說的“內(nèi)存泄漏”問題水孩。如下圖:
[圖片上傳失敗...(image-56fb94-1510574775013)]
5. 分代收集算法
? 當(dāng)前的商業(yè)虛擬機(jī)都采用的是”分代收集“算法,一般是把java堆分成新生代和老生代琐驴,這樣就可以根據(jù)各個年代的特點采用最適當(dāng)?shù)睦占惴ǎ律谐颖辏瑢ο蟠蠖嗍恰背λ馈翱梢圆捎脧?fù)制算法绝淡,而老年代的對象存活率比較高,而且沒有擔(dān)辈越空間進(jìn)行內(nèi)存分配牢酵,就要采用”標(biāo)記-清除算法“或者”標(biāo)記-整理“算法。
二衙猪、Java垃圾回收
1. Java的內(nèi)存分布
[圖片上傳失敗...(image-5469f-1510574775013)]
其中馍乙,堆內(nèi)存分為年輕代和年老代布近,非堆內(nèi)存主要是Permanent區(qū)域,主要用于存儲一些類的元數(shù)據(jù)丝格,常量池等信息撑瞧。而年輕代又分為兩種,一種是Eden區(qū)域显蝌,另外一種是兩個大小對等的Survivor區(qū)域预伺。
2. Java年輕代垃圾回收機(jī)制
[圖片上傳失敗...(image-3bdb3-1510574775013)]
? 部分的新創(chuàng)建對象分配在新生代。因為大部分對象很快就會變得不可達(dá)曼尊,所以它們被分配在新生代酬诀,然后消失不再。當(dāng)對象從新生代移除時骆撇,我們稱之為"Minor GC"瞒御。新生代使用的是復(fù)制收集算法。
? 新生代劃分為三個部分:分別為Eden神郊、Survivor from肴裙、Survivor to,大小比例為8:1:1(為了防止復(fù)制收集算法的浪費(fèi)內(nèi)存過大)屿岂。每次只使用Eden和其中的一塊Survivor践宴,回收時將存活的對象復(fù)制到另一塊Survivor中,這樣就只有10%的內(nèi)存被浪費(fèi)爷怀,但是如果存活的對象總大小超過了Survivor的大小阻肩,那么就把多出的對象放入老年代中。
在三個區(qū)域中有兩個是Survivor區(qū)运授。對象在三個區(qū)域中的存活過程如下:
- 大多數(shù)新生對象都被分配在Eden區(qū)烤惊。
- 第一次GC過后Eden中還存活的對象被移到其中一個Survivor區(qū)。
- 再次GC過程中吁朦,Eden中還存活的對象會被移到之前已移入對象的Survivor區(qū)柒室。
- 一旦該Survivor區(qū)域無空間可用時,還存活的對象會從當(dāng)前Survivor區(qū)移到另一個空的Survivor區(qū)逗宜。而當(dāng)前Survivor區(qū)就會再次置為空狀態(tài)雄右。
- 經(jīng)過數(shù)次(默認(rèn)是15次)在兩個Survivor區(qū)域移動后還存活的對象最后會被移動到老年代。
如上所述纺讲,兩個Survivor區(qū)域在任何時候必定有一個保持空白擂仍。如果同時有數(shù)據(jù)存在于兩個Survivor區(qū)或者兩個區(qū)域的的使用量都是0,則意味著你的系統(tǒng)可能出現(xiàn)了運(yùn)行錯誤熬甚。
3. Java老年代垃圾回收機(jī)制
? 存活在新生代中但未變?yōu)椴豢蛇_(dá)的對象會被復(fù)制到老年代逢渔。一般來說老年代的內(nèi)存空間比新生代大,所以在老年代GC發(fā)生的頻率較新生代低一些乡括。當(dāng)對象從老年代被移除時肃廓,我們稱之為 "Major GC"(或者Full GC)智厌。 老年代使用標(biāo)記-清理或標(biāo)記-整理算法
空間分配擔(dān)保
在發(fā)生Minor GC前,虛擬機(jī)會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間盲赊。
如果大于铣鹏,那么Minor GC可以確保是安全的。
-
如果小于角钩,虛擬機(jī)會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)任失敗吝沫。
- 如果允許,那么會繼續(xù)檢查老年代最大可用連續(xù)空間是否大于歷次晉升老年代對象的平均大小
- 如果大于递礼,將嘗試著進(jìn)行一次Minor GC惨险,盡管這次Minor GC是有風(fēng)險的
- 如果小于,進(jìn)行一次Full GC
- 如果不允許脊髓,也要改為進(jìn)行一次Full GC
? 前面提到過辫愉,新生代使用復(fù)制收集算法,但為了內(nèi)存利用率将硝,只使用其中一個Survivor空間來作為輪換備份恭朗,因此當(dāng)出現(xiàn)大量對象在Minor GC后仍然存活的情況時(最極端就是內(nèi)存回收后新生代中所有對象都存活),就需要老年代進(jìn)行分配擔(dān)保依疼,讓Survivor無法容納的對象直接進(jìn)入老年代痰腮。與生活中的貸款擔(dān)保類似,老年代要進(jìn)行這樣的擔(dān)保律罢,前提是老年代本身還有容納這些對象的剩余空間膀值,一共有多少對象會活下來,在實際完成內(nèi)存回收之前是無法明確知道的误辑,所以只好取之前每一次回收晉升到老年代對象容量的平均大小值作為經(jīng)驗值沧踏,與老年代的剩余空間進(jìn)行比較,決定是否進(jìn)行Full GC來讓老年代騰出更多空間巾钉。
? 取平均值進(jìn)行比較其實仍然是一種動態(tài)概率的手段翘狱,也就是說如果某次Minor GC存活后的對象突增,遠(yuǎn)遠(yuǎn)高于平均值的話砰苍,依然會導(dǎo)致?lián)J潦匈。℉andle Promotion Failure)。如果出現(xiàn)了HandlePromotionFailure失敗赚导,那就只好在失敗后重新發(fā)起一次Full GC历等。雖然擔(dān)保失敗時繞的圈子是最大的,但大部分情況下都還是會將HandlePromotionFailure開關(guān)打開辟癌,避免Full GC過于頻繁。
- 如果允許,那么會繼續(xù)檢查老年代最大可用連續(xù)空間是否大于歷次晉升老年代對象的平均大小
2.Java垃圾收集器
-
Serial收集器(Serial/Serial Old)
Serial是一個單線程的收集器荐捻,但它的“單線程”意義并不僅僅說明它只會使用一個CPU或一條手機(jī)此案成去完成垃圾和收集工作黍少,更重要的是它進(jìn)行垃圾收集時寡夹,必須暫停其他所有的工作線程,直到它收集結(jié)束厂置。
img -
ParNew收集器
ParNew收集器其實就是Serial收集器的多線程版本菩掏。
它是運(yùn)行在Server模式下的虛擬機(jī)中首選的新生代收集器,其中有一個與性能無關(guān)但很重要的原因是:除了Serial收集器外昵济,目前只有它能與CMS收集器配合工作智绸。
img -
Parallel Scavenge收集器
? 該收集器也是一個新生代的垃圾收集器,他也是使用復(fù)制算法的收集器访忿,又是一個并行的垃圾收集器瞧栗。該收集器的特點是他的關(guān)注點與其他的收集器不同,CMS等收集器的關(guān)注點是盡可能縮短垃圾回收時用戶線程的停頓時間海铆,而parallel Scavenge收集器的目標(biāo)是達(dá)到一個可控制的吞吐量迹恐。所謂吞吐量就是CPU用于運(yùn)行代碼的時間與CPU總消耗時間的比值,即吞吐量=運(yùn)行用戶代碼時間/(運(yùn)行用戶代碼時間+垃圾回收時間)卧斟,比如虛擬機(jī)總共運(yùn)行100分鐘殴边,垃圾回收占用了1分鐘,那么吞吐量就是99%珍语。
-
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本锤岸,使用多線程和“標(biāo)記-整理”算法。
img -
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標(biāo)的收集器板乙。CMS是基于“標(biāo)記-清除”算法實現(xiàn)的是偷,它的運(yùn)作過程相對于前面幾種收集器來說更復(fù)雜一些,整個過程分為4個步驟亡驰,包括:
- 初始標(biāo)記(CMS initial mark)
- 并發(fā)標(biāo)記(CMS concurrent mark)
- 重新標(biāo)記(CMS remark)
- 并發(fā)清除(CMS concurrent sweep)
其中晓猛,初始標(biāo)記、重新標(biāo)記這兩個步驟仍然需要”Stop The world”凡辱。初始標(biāo)記僅僅只是標(biāo)記一下GC Roots Tracing的過程戒职,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄,這個階段的停頓時間一般會比初始標(biāo)記階段稍長一些透乾,但遠(yuǎn)比并發(fā)標(biāo)記的時間短洪燥。
由于整個過程中耗時最長的并發(fā)標(biāo)記和并發(fā)清除過程收集器線程都可以與用戶線程一起工作,所以乳乌,從總體上來說捧韵,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的。
imgCMS的優(yōu)勢:并發(fā)收集汉操、低停頓再来。
CMS的缺點:
- 對CPU資源非常敏感。CMS默認(rèn)啟動的回收線程數(shù)是(CPU數(shù)量 + 3)/4,并發(fā)回收時垃圾收集線程所占CPU資源隨著CPU數(shù)量的增加而下降,而且在CPU不足4個時芒篷,CMS對用戶程序的影響就可能變得很大搜变,導(dǎo)致執(zhí)行速度降低。
- CMS收集器無法處理浮動垃圾针炉,可能出現(xiàn)“Concurrent Mode Failure”失敗而導(dǎo)致另一次Full GC的產(chǎn)生挠他。
- CMS是一款基于“標(biāo)記-清除”算法實現(xiàn)的收集器,這意味著收集結(jié)束時會有大量空間碎片產(chǎn)生篡帕≈城郑空間碎片太多的時候,將會給大對象分配帶來很大麻煩镰烧。
-
G1收集器
G1是一款面向服務(wù)端應(yīng)用的垃圾收集器拢军。HOtSpot開發(fā)團(tuán)隊賦予它的使命是未來可以替換掉CMS收集器。
G1具備如下特點:
- 并行與并發(fā):G1能充分利用多CPU拌滋、多核環(huán)境下的硬件優(yōu)勢朴沿,使用多個CPU來縮短Stop-The-World停頓的時間,部分其他收集器原本需要停頓Java線程執(zhí)行的GC動作败砂,G1收集器仍然可以通過并發(fā)的方式讓Java程序繼續(xù)執(zhí)行赌渣。
- 分代收集:雖然G1可以不需要其他收集器配合就能獨(dú)立管理整個GC堆,但它能夠采用不同的方式去處理新創(chuàng)建的對象和已經(jīng)存活了一段時間昌犹、熬過多次GC的就對象以獲取更好的收集效果坚芜。
- 空間整合:G1從整體上來看是基于“標(biāo)記-整理”算法實現(xiàn)的收集器,從局部(兩個Region之間)上來看是基于“復(fù)制”算法實現(xiàn)的斜姥,這意味著G1運(yùn)作期間不會產(chǎn)生內(nèi)存空間碎片鸿竖,收集后能提供規(guī)整的可用內(nèi)存。
- 可預(yù)測的停頓:這是G1相對于CMS的另一大優(yōu)勢铸敏。
?
G1垃圾收集器和CMS垃圾收集器有幾點不同缚忧。首先,最大的不同是內(nèi)存的組織方式變了杈笔。Eden闪水,Survivor和Tenured等內(nèi)存區(qū)域不再是連續(xù)的了,而是變成了一個個大小一樣的region - 每個region從1M到32M不等蒙具。
?
[圖片上傳失敗...(image-f2affc-1510574775013)]
?
一個region有可能屬于Eden球榆,Survivor或者Tenured內(nèi)存區(qū)域。圖中的E表示該region屬于Eden內(nèi)存區(qū)域禁筏,S表示屬于Survivor內(nèi)存區(qū)域持钉,T表示屬于Tenured內(nèi)存區(qū)域。圖中空白的表示未使用的內(nèi)存空間篱昔。G1垃圾收集器還增加了一種新的內(nèi)存區(qū)域每强,叫做Humongous內(nèi)存區(qū)域,如圖中的H塊。這種內(nèi)存區(qū)域主要用于存儲大對象-即大小超過一個region大小的50%的對象空执。
?
在G1垃圾收集器中窘茁,年輕代的垃圾回收過程跟PS垃圾收集器和CMS垃圾收集器差不多。
[圖片上傳失敗...(image-5a0154-1510574775013)]
?
對于年老代上的垃圾收集脆烟,G1垃圾收集器也分為4個階段,基本跟CMS垃圾收集器一樣房待,但略有不同:
Initial Mark階段 - 同CMS垃圾收集器的Initial Mark階段一樣邢羔,G1也需要暫停應(yīng)用程序的執(zhí)行,它會標(biāo)記從根對象出發(fā)桑孩,在根對象的第一層孩子節(jié)點中標(biāo)記所有可達(dá)的對象拜鹤。但是G1的垃圾收集器的Initial Mark階段是跟minor gc一同發(fā)生的。也就是說流椒,在G1中敏簿,你不用像在CMS那樣,單獨(dú)暫停應(yīng)用程序的執(zhí)行來運(yùn)行Initial Mark階段宣虾,而是在G1觸發(fā)minor gc的時候一并將年老代上的Initial Mark給做了惯裕。
Concurrent Mark階段 - 在這個階段G1做的事情跟CMS一樣。但G1同時還多做了一件事情绣硝,那就是蜻势,如果在Concurrent Mark階段中,發(fā)現(xiàn)哪些Tenured region中對象的存活率很小或者基本沒有對象存活鹉胖,那么G1就會在這個階段將其回收掉握玛,而不用等到后面的clean up階段。這也是Garbage First名字的由來甫菠。同時挠铲,在該階段,G1會計算每個 region的對象存活率寂诱,方便后面的clean up階段使用 拂苹。
Remark階段 - 在這個階段G1做的事情跟CMS一樣, 但是采用的算法不同,能夠在Remark階段更快的標(biāo)記可達(dá)對象刹衫。
-
Clean up/Copy階段 - 在G1中醋寝,沒有CMS中對應(yīng)的Sweep階段。相反 它有一個Clean up/Copy階段带迟,在這個階段中,G1會挑選出那些對象存活率低的region進(jìn)行回收音羞,這個階段也是和minor gc一同發(fā)生的,如下圖所示:
[圖片上傳失敗...(image-ed332d-1510574775013)]
從上可以看到,由于Initial Mark階段和Clean up/Copy階段都是跟minor gc同時發(fā)生的仓犬,相比于CMS嗅绰,G1暫停應(yīng)用程序的時間更少,從而提高了垃圾回收的效率。
?