新生代和老年的垃圾回收算法介紹

垃圾回收算法

上一篇文章我們介紹了堆內(nèi)存分配、jvm分代模型、垃圾回收觸發(fā)條件以及jvm的一些核心參數(shù)設(shè)置雨女,理解了這些內(nèi)容之后,我們從本文開始介紹jvm的垃圾回收算法阳准。

復(fù)制算法

針對新生代的垃圾回收算法叫做\color{green}{復(fù)制算法}氛堕。簡單來說,首先把新生代的內(nèi)存分為兩塊野蝇。當(dāng)有對象被創(chuàng)建就會(huì)分配在其中一塊內(nèi)存空間里讼稚,對象被方法局部變量來引用。隨著系統(tǒng)的運(yùn)行绕沈,大量的對象被創(chuàng)建并分配在新生代的其中一塊內(nèi)存區(qū)域里锐想,而且分配過后,很快就失去了局部變量或者類靜態(tài)變量的引用乍狐,成為了垃圾對象赠摇。當(dāng)新生代那塊被分配對象的內(nèi)存區(qū)域基本快滿了,再次要分配對象的時(shí)候發(fā)現(xiàn)內(nèi)存空間不足了浅蚪,那么此時(shí)就會(huì)觸發(fā)Minor GC回收掉新生代那塊被使用的內(nèi)存空間的垃圾對象藕帜。
\color{green}{一種不太好的垃圾回收思路}
假設(shè)現(xiàn)在采用的垃圾回收思路就是直接對被使用的那塊內(nèi)存區(qū)域中的垃圾對象進(jìn)行標(biāo)記,找出可以被垃圾回收的對象掘鄙,然后直接對那塊內(nèi)存區(qū)域中的對象進(jìn)行垃圾回收耘戚,把內(nèi)存空出來嗡髓。
\color{blue}{我們思考一下操漠,這種思路好嗎}
用這種思路去做垃圾回收饿这,可能回收完畢之后\color{green}{造成大量的內(nèi)存碎片}浊伙。那么什么是內(nèi)存碎片呢?我們下面來看一張圖长捧,這樣會(huì)更加直觀嚣鄙,示例圖如下:

neicun.jpg

上面用紅圈標(biāo)記出來的區(qū)域就是所謂的內(nèi)存碎片。在各種凌亂的存活對象的中間串结,出現(xiàn)了大量的紅圈圈出來的內(nèi)存碎片哑子。這些內(nèi)存碎片大小不一樣舅列,有的可能很大,有的可能很小卧蜓。
那么內(nèi)存碎片太多會(huì)造成什么問題呢帐要?
\color{red}{內(nèi)存浪費(fèi)!}

啥意思弥奸?比如現(xiàn)在打算分配一個(gè)新的對象榨惠,嘗試在上圖那塊被使用的內(nèi)存區(qū)域里分配,雖然所有的內(nèi)存碎片加起來其實(shí)有很大的一塊內(nèi)存盛霎,但是因?yàn)檫@些內(nèi)存都是碎片式分散的赠橙,可能會(huì)因?yàn)樗槠嗟木壒剩瑢?dǎo)致沒有一塊完整的足夠的內(nèi)存空間來分配新的對象愤炸。
所以這種直接對一塊內(nèi)存空間回收掉垃圾對象期揪,保留存活對象的方法絕對是不可取的。因?yàn)閮?nèi)存碎片太多就是它最大的問題规个。
\color{green}{一個(gè)合理的垃圾回收思路}

那么有沒有一種合理的思路來進(jìn)行垃圾回收呢横侦?
\color{red}{當(dāng)然有!}
這個(gè)時(shí)候上圖中一直沒有派上用場的另外一塊空白的內(nèi)存區(qū)域就出場了绰姻。首先枉侧,并不是按照上述思路直接對已經(jīng)使用的那塊內(nèi)存把垃圾對象全部回收掉,然后保留全部存活對象狂芋。而是先對那塊在使用的內(nèi)存空間標(biāo)記出里面哪些對象是不能進(jìn)行垃圾回收的榨馁,也就是要存活的對象,然后先把那些存活的對象轉(zhuǎn)移到另外一塊空白的內(nèi)存中帜矾。如下圖:
xinshengdai.jpg

這樣就可以讓被轉(zhuǎn)移的那塊內(nèi)存區(qū)域沒有什么內(nèi)存碎片翼虫,對象都是按順序排列在這塊內(nèi)存里的。這時(shí)候再一次性把原來使用的那塊內(nèi)存區(qū)域中的垃圾對象全部一掃而空屡萤,空出來一塊內(nèi)存區(qū)域珍剑。這就是所謂的
\color{green}{復(fù)制算法}
,把新生代內(nèi)存劃分為兩塊內(nèi)存區(qū)域死陆,然后只使用其中一塊內(nèi)存招拙,等那塊內(nèi)存快滿的時(shí)候,就把里面存活的對象一次性轉(zhuǎn)移到另外一塊內(nèi)存區(qū)域措译,保證沒有內(nèi)存碎片别凤。接著再一次性回收原來那塊內(nèi)存區(qū)域的垃圾對象,再次空出來一塊內(nèi)存區(qū)域领虹。兩塊內(nèi)存區(qū)域就這么重復(fù)著循環(huán)使用规哪。
\color{red}{復(fù)制算法有什么缺點(diǎn)?}

復(fù)制算法的缺點(diǎn)其實(shí)非常明顯塌衰,如果按照上述思路诉稍,大家會(huì)發(fā)現(xiàn)蝠嘉,假設(shè)我們給新生代1G的內(nèi)存空間,那么只有512MB的內(nèi)存空間是可以用的杯巨,另外512MB的內(nèi)存空間是一直要空著的是晨,從始至終,就只有一半的內(nèi)存可以使用舔箭,這樣的算法顯然對內(nèi)存的使用效率太低了罩缴。
\color{green}{復(fù)制算法的優(yōu)化:Eden去和Survivor區(qū)}

我們之前分析過,絕大多數(shù)的對象都是存活周期非常短的對象层扶,可能被創(chuàng)建出來1毫秒之后就沒人引用了箫章,它就是垃圾對象了,所以大家可以想象一下镜会,可能一次新生代垃圾回收過后檬寂,99%的對象其實(shí)都被垃圾回收了,就1%的對象存活下來戳表,可能就是一些長期存活的對象桶至,或者還沒有使用完的對象。所以實(shí)際上真正的復(fù)制算法會(huì)做出如下優(yōu)化匾旭,把新生代內(nèi)存區(qū)域劃分為三塊:
eden.jpg

1個(gè)Eden區(qū)镣屹,2個(gè)Survivor區(qū),其中Eden區(qū)占80%內(nèi)存空間价涝,每一塊Survivor區(qū)各占10%內(nèi)存空間女蜈,比如說Eden區(qū)有800MB內(nèi)存,每一塊Survivor區(qū)就100MB內(nèi)存色瘩。平時(shí)可以使用的就是Eden區(qū)和其中一塊Survivor區(qū)伪窖,那么相當(dāng)于就是有900MB的內(nèi)存是可以使用的。
但是剛開始的時(shí)候?qū)ο蠖际欠峙湓贓den區(qū)內(nèi)的居兆,如果Eden區(qū)快滿了覆山,此時(shí)就會(huì)觸發(fā)垃圾回收,就會(huì)把Eden區(qū)中的存活對象都一次性轉(zhuǎn)移到一塊空著的Survivor區(qū)泥栖。接著Eden區(qū)就會(huì)被清空簇宽,然后再次分配新對象到Eden區(qū),然后Eden區(qū)和一塊Survivor區(qū)里是有對象的聊倔,其中Survivor區(qū)里放的是上一次Minor GC后存活的對象晦毙。
如果Eden區(qū)再次被放滿,那么再次觸發(fā)Minor GC就會(huì)把Eden區(qū)和放著上一次Minor GC后存活對象的Survivor區(qū)內(nèi)的存活對象耙蔑,轉(zhuǎn)移到另外一塊Survivor區(qū)里,然后再一次性把Eden區(qū)和之前使用的Survivor區(qū)里的垃圾對象全部回收掉孤荣。

對象什么時(shí)候從年輕代進(jìn)入老年代

\color{red}{新生代里的對象一般在什么情況下會(huì)進(jìn)入老年代甸陌?}须揣。\color{green}{答案是躲過15次GC之后進(jìn)入老年代。}其實(shí)大家可以理解為我們寫的系統(tǒng)剛啟動(dòng)的時(shí)候钱豁,創(chuàng)建的各種各樣的對象耻卡,都是分配在新生代里的,然后隨著系統(tǒng)的運(yùn)行牲尺,新生代就滿了卵酪,此時(shí)就會(huì)觸發(fā)Minor GC,可能就1%的少量存活對象轉(zhuǎn)移到空著的Survivor區(qū)中谤碳。隨著系統(tǒng)繼續(xù)運(yùn)行溃卡,不斷重復(fù)這個(gè)過程。
那么之前給大家講過蜒简,我們寫的系統(tǒng)中有些對象是長期存在的瘸羡,它是不會(huì)輕易的被回收掉的,比如下面這段代碼:

public class User {
      private static Role role = new Role();
}

只要“User”這個(gè)類還存在搓茬,那么它的靜態(tài)變量“role”就會(huì)長期引用“Role”實(shí)例對象犹赖,所以你無論新生代怎么垃圾回收,類似這種對象都不會(huì)被回收掉卷仑。這類對象每次在新生代里躲過一次GC被轉(zhuǎn)移到一塊Survivor區(qū)域中后峻村,它的年齡就會(huì)增長一歲,默認(rèn)的設(shè)置下锡凝,當(dāng)對象的年齡達(dá)到15歲的時(shí)候雀哨,也就是躲過15次GC的時(shí)候,它就會(huì)轉(zhuǎn)移到老年代里去私爷。這個(gè)具體是多少歲進(jìn)入老年代雾棺,可以通過jvm參數(shù)“-XX:MaxTenuringThreshold”來設(shè)置,默認(rèn)是15歲衬浑。

\color{green}{動(dòng)態(tài)對象年齡判斷}
根據(jù)對象年齡捌浩,有另外一個(gè)規(guī)則可以讓對象進(jìn)入老年代,不用等15次GC過后才可以工秩。大致的規(guī)則是尸饺,假如說當(dāng)前放對象的Survivor區(qū)域里一批對象的總大小大于這個(gè)Survivor區(qū)域的內(nèi)存大小的50%,那么大于等于這批對象年齡的對象就可以直接進(jìn)入老年代了助币。舉個(gè)栗子:假設(shè)Survivor區(qū)大小100MB浪听,有兩個(gè)對象,這倆對象的年齡一樣眉菱,都是2歲迹栓,然后兩個(gè)對象加起來超過了50MB,超過了Survivor區(qū)的內(nèi)存大小的一半了俭缓,這個(gè)時(shí)候克伊,Survivor區(qū)里的大于等于2歲的對象就要全部進(jìn)入老年代酥郭。這就是\color{green}{動(dòng)態(tài)對象年齡判斷}的規(guī)則,這條規(guī)則也會(huì)讓一些新生代的對象進(jìn)入老年代愿吹。
實(shí)際上這個(gè)規(guī)則運(yùn)行的時(shí)候是如下邏輯:年齡1+年齡2+年齡N的多個(gè)年齡對象總和超過了Survivor區(qū)域的50%不从,此時(shí)就會(huì)把年齡N以上的對象都放入老年代。
其實(shí)說白了犁跪,無論是15歲的那個(gè)規(guī)則椿息,還是動(dòng)態(tài)年齡判斷的規(guī)則,都是希望那些可能是長期存活的對象盡早進(jìn)入老年代坷衍。

\color{red}{大對象直接進(jìn)入老年代}
有一個(gè)jvm參數(shù)叫“-XX:PretenureSizeThreshold”寝优,可以把它的值設(shè)置為字節(jié)數(shù),比如“1048576”字節(jié)惫叛,就是1MB倡勇。它的意思是如果你要?jiǎng)?chuàng)建一個(gè)大于這個(gè)大小的對象,比如一個(gè)超大的數(shù)組嘉涌,此時(shí)就直接把這個(gè)大對象放到老年代妻熊,壓根就不會(huì)經(jīng)過新生代。之所以這么做仑最,就是要避免新生代里出現(xiàn)那種大對象扔役,屢次躲過GC,還得把他在兩個(gè)Survivor區(qū)里來回復(fù)制多次之后才能進(jìn)入老年代警医,那么大的一個(gè)對象在內(nèi)存里來回復(fù)制亿胸,是相當(dāng)耗費(fèi)時(shí)間的。所以說這也是一個(gè)對象進(jìn)入老年代的規(guī)則预皇。

\color{red}{Minor GC后的對象太多無法放入Survivor區(qū)怎么辦侈玄?}
現(xiàn)在有一個(gè)比較大的問題,就是如果在Minor GC之后發(fā)現(xiàn)剩余的存活對象太多了吟温,無法放入另外一塊Survivor區(qū)怎么辦序仙?假設(shè)Survivor區(qū)有100MB內(nèi)存,發(fā)生GC的時(shí)候發(fā)現(xiàn)Eden區(qū)里有超過150MB的存活對象鲁豪,沒辦法放入Survivor區(qū)里潘悼,此時(shí)怎么辦?\color{green}{這個(gè)時(shí)候就必須得把這些對象直接轉(zhuǎn)移到老年代爬橡。}

老年代空間分配擔(dān)保規(guī)則治唤。
這個(gè)時(shí)候大家又想提另外一個(gè)問題,如果老年代里的空間也不夠放這些對象呢糙申?別急宾添,我們來一步一步分析。首先,在執(zhí)行任何一次Minor GC之前辞槐,jvm會(huì)先檢查一下老年代可用的內(nèi)存空間是否大于新生代所有對象的總大小掷漱。為什么檢查這個(gè)呢粘室?因?yàn)樽顦O端的情況下榄檬,可能新生代Minor GC過后所有對象都存活了下來,那豈不是新生代所有對象全部要進(jìn)入老年代衔统?
如果說發(fā)現(xiàn)老年代的可用內(nèi)存大小是大于新生代所有對象的鹿榜,此時(shí)就可以放心大膽的對新生代發(fā)起一次Minor GC了,因?yàn)榧词筂inor GC之后所有對象都存活了锦爵,Survivor區(qū)放不下了舱殿,也可以轉(zhuǎn)移到老年代里去。\color{red}{但是险掀,}假設(shè)執(zhí)行Minor GC之前發(fā)現(xiàn)老年代的可用內(nèi)存已經(jīng)小于了新生代的全部對象大小了沪袭,那么這個(gè)時(shí)候是不是有可能發(fā)生在Minor GC之后新生代的對象全部存活下來,然后全部需要轉(zhuǎn)移到老年代樟氢,但是老年代空間又不夠的情況冈绊?理論上是有這種可能的!

這個(gè)時(shí)候jvm就會(huì)看一個(gè)叫“-XX:-HandlePromotionFailure”的參數(shù)是否設(shè)置了埠啃,如果有這個(gè)參數(shù)死宣,那么就會(huì)繼續(xù)進(jìn)行下一步判斷,就是看老年代的內(nèi)存大小是否大于之前每一次Minor GC后進(jìn)入老年代的對象的平均大小碴开。

舉個(gè)栗子毅该,之前每次Minor GC后,平均有10MB左右的對象會(huì)進(jìn)入老年代潦牛,那么此時(shí)老年代可用內(nèi)存大于10MB眶掌,這就說明,很可能這次Minor GC過后也是差不多10MB左右的對象會(huì)進(jìn)入老年代巴碗,此時(shí)老年代空間是夠的朴爬。

如果上面那個(gè)步驟判斷失敗了,或者是“-XX:-HandlePromotionFailure”參數(shù)沒設(shè)置良价,此時(shí)就會(huì)直接出發(fā)一次 “Full GC”寝殴,就是對老年代進(jìn)行垃圾回收,盡量騰出來一些內(nèi)存空間明垢,然后再執(zhí)行Minor GC蚣常。如果上面兩個(gè)步驟都成功了,那么就是說可以冒點(diǎn)風(fēng)險(xiǎn)嘗試一下Minor GC痊银。此時(shí)進(jìn)行Minor GC有幾種可能抵蚊。

第一種可能,Minor GC過后,剩余的存活對象的大小贞绳,小于Survivor區(qū)的內(nèi)存空間谷醉,那么此時(shí)存活對象進(jìn)入Survivor區(qū)域即可。
第二種可能冈闭,Minor GC過后俱尼,剩余的存活對象的大小,大于Survivor區(qū)域的內(nèi)存空間萎攒,但是小于老年代可用內(nèi)存空間遇八,此時(shí)直接進(jìn)入老年代即可。
第三種可能耍休,Minor GC過后刃永,剩余的存活對象的大小,大于Survivor區(qū)域的內(nèi)存空間羊精,也大于老年代可用內(nèi)存空間斯够,此時(shí)老年代都放不下這些存活對象了,就會(huì)發(fā)生“Handle Promotion Failure”的情況喧锦,這個(gè)時(shí)候就會(huì)觸發(fā)一次“Full GC”读规。

Full GC就是對老年代進(jìn)行垃圾回收,同時(shí)也一般會(huì)對新生代進(jìn)行垃圾回收裸违。因?yàn)檫@個(gè)時(shí)候必須把老年代里沒人引用的對象給回收掉掖桦,然后才可能讓Minor GC過后剩余的存活對象進(jìn)入老年代里。如果Full GC過后老年代還是沒有足夠的空間存放Minor GC過后的剩余存活對象供汛,那么此時(shí)就會(huì)導(dǎo)致所謂的“OOM”內(nèi)存溢出了枪汪。

適合老年代垃圾回收的算法——標(biāo)記整理算法

簡單來說,老年代采取的是\color{green}{標(biāo)記整理算法}怔昨。大家看下圖:

lnd.jpg

首先雀久,標(biāo)記出來老年代當(dāng)前存活的對象,這些對象可能是東一個(gè)西一個(gè)的趁舀。接著赖捌,會(huì)讓這些存活對象在內(nèi)存里進(jìn)行移動(dòng),把存活對象盡量都挪動(dòng)到一邊去矮烹,讓存活對象緊湊的靠在一起越庇,避免垃圾回收后出現(xiàn)過多的內(nèi)存碎片。然后再一次性把垃圾對象都回收掉奉狈。這里要注意卤唉,老年代的垃圾回收算法的速度至少比新生代的垃圾回收算法的速度慢10倍。如果系統(tǒng)頻繁出現(xiàn)老年代的Full GC垃圾回收仁期,會(huì)導(dǎo)致系統(tǒng)性能被嚴(yán)重影響桑驱,出現(xiàn)頻繁卡頓的情況竭恬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者熬的。
  • 序言:七十年代末痊硕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子押框,更是在濱河造成了極大的恐慌岔绸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强戴,死亡現(xiàn)場離奇詭異亭螟,居然都是意外死亡挡鞍,警方通過查閱死者的電腦和手機(jī)骑歹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墨微,“玉大人道媚,你說我怎么就攤上這事∏滔兀” “怎么了最域?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長锈麸。 經(jīng)常有香客問我镀脂,道長,這世上最難降的妖魔是什么忘伞? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任薄翅,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己帝嗡,他們只是感情好轧拄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著床蜘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上但荤,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音涧至,去河邊找鬼腹躁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛化借,可吹牛的內(nèi)容都是我干的潜慎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铐炫!你這毒婦竟也來了垒手?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤倒信,失蹤者是張志新(化名)和其女友劉穎科贬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳖悠,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榜掌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乘综。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憎账。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卡辰,靈堂內(nèi)的尸體忽然破棺而出胞皱,到底是詐尸還是另有隱情,我是刑警寧澤九妈,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布反砌,位于F島的核電站,受9級特大地震影響萌朱,放射性物質(zhì)發(fā)生泄漏宴树。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一晶疼、第九天 我趴在偏房一處隱蔽的房頂上張望酒贬。 院中可真熱鬧,春花似錦冒晰、人聲如沸同衣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耐齐。三九已至,卻和暖如春蒋情,著一層夾襖步出監(jiān)牢的瞬間埠况,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工棵癣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辕翰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓狈谊,卻偏偏與公主長得像喜命,于是被迫代替她去往敵國和親沟沙。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354