- 標(biāo)記 - 清除算法
- 復(fù)制算法
- 標(biāo)記 - 整理算法
- 分代收集算法
1 標(biāo)記 - 清除算法
實現(xiàn)原理
- 標(biāo)記出所有需要回收的對象蟀给。
- 統(tǒng)一回收所有被標(biāo)記的對象黎做。
特點
- 標(biāo)記和清除效率不高。
- 產(chǎn)生大量不連續(xù)的內(nèi)存碎片松忍。
- 標(biāo)記清除算法是最基礎(chǔ)的收集算法蒸殿,后面的幾種算法都是基于這種思路并對其不足進(jìn)行改進(jìn)而得到的。 算法分為“標(biāo)記”和“清除”兩個階段鸣峭。
- 標(biāo)記 : 首先標(biāo)記處所有需要回收的對象宏所,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。標(biāo)記的判定就是通過可達(dá)性分析算法分析出可回收對象摊溶。
- 清除 : 標(biāo)記完成后會回收對象爬骤。但是這其中存在著兩個不足問題 : 第一是標(biāo)記和清除這兩個過程效率不高。其次是清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片莫换。
- 清除 : 標(biāo)記完成后會回收對象霞玄。
- 但是這其中存在著兩個不足問題 : 第一是標(biāo)記和清除這兩個過程效率不高。其次是清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片拉岁。
- 空間碎片太多可能會導(dǎo)致以后在程序運行過程中需要分配比較大對象時坷剧,無法找到足夠連續(xù)內(nèi)存。從而提前觸發(fā)依次GC喊暖。
160f7b667fa1cd3b.png
- 相當(dāng)于生活中標(biāo)記不需要的物品惫企,然后找?guī)讉€人把這些東西丟了。 但是由于房間的空間是有限的陵叽,如果一直沒有整理房間狞尔,如果有大件家具搬進(jìn)來時丛版,沒有足夠的空間可能要再標(biāo)記清除一下不需要的物品,如果還是沒有辦法放下這件大家具偏序,拋出OOM異常页畦。丟(Out)到(Of)門外(Memory)。
2 復(fù)制算法
實現(xiàn)原理
- 將內(nèi)存劃分為大小相等的兩塊禽车。
- 一塊內(nèi)存用完之后復(fù)制存活對象至另一塊寇漫。
- 清理另一塊內(nèi)存。
特點
- 實現(xiàn)簡單殉摔,運行高效州胳。
- 浪費一半空間,代價大逸月。
- 為了解決效率問題栓撞,復(fù)制收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊碗硬,每次只使用其中的一塊瓤湘。
- 當(dāng)這一塊的內(nèi)存用完了,就將還存活的對象復(fù)制到另一塊上面恩尾,然后把已使用過的那一塊內(nèi)存空間全部清理弛说。
- 這樣便不用考慮內(nèi)存碎片問題,只要移動堆頂指針翰意,重新按順序分配內(nèi)存即可木人,實現(xiàn)簡單,運行高效冀偶。
- 只是這種算法的代價是將活動使用的內(nèi)存縮小為原來的一半醒第。
160f7b667fa1cd3b.png
- 復(fù)制算法當(dāng)存活對象越少(或者垃圾很容易產(chǎn)生)時,它的效率越高进鸠。
也就是你只要將一些少量需要用到的對象丟到另一片干凈的內(nèi)存中稠曼,就可以輕松的大掃除。
換句話說客年,如果你需要保留的對象很多霞幅,那么便需要又費力又頻繁的搬動到另一塊內(nèi)存中。
- 復(fù)制收集算法在對象存活率較高時就要進(jìn)行較多的操作搀罢,效率就會變得很低蝗岖。并且只能使用50%的空間。
3 標(biāo)記 - 整理算法
實現(xiàn)原理
- 標(biāo)記過程與 ”標(biāo)記-清除“ 算法一樣榔至。
- 存活對象往一端進(jìn)行移動抵赢。
- 清理其余內(nèi)存。
特點
- 避免 ”標(biāo)記-清除” 算法導(dǎo)致的內(nèi)存碎片。
- 避免復(fù)制算法的空間浪費铅鲤。
- 標(biāo)記 : 因為存活的對象比較多划提,我們將這些對象標(biāo)記起來。然后接下來整理到一塊邢享。
- 整理 : 讓所有標(biāo)記過的對象都向一端移動鹏往,然后直接清理掉端邊界以外的內(nèi)存。(按內(nèi)存地址一次排列)
160f7b66a84fa47a.png
4 分代收集算法
特點
- 結(jié)合多種收集算法的優(yōu)勢骇塘。
- 新生代對象存活率低 => “復(fù)制” 算法(注意這里每一次的復(fù)制比例都是可以調(diào)整的伊履,如一次僅復(fù)制 30% 的存活對象)。
- 老年代對象存活率高 => “標(biāo)記-整理” 算法款违。
當(dāng)前很多商業(yè)虛擬機(jī)都采用分代收集(Generational Collection)算法作為垃圾收集器唐瀑。
- 這種算法只是根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊,進(jìn)而采用不同的回收算法的策略插爹。
- 一般是把Java堆分成新生代和老年代哄辣。
- 新生代 : 對象朝生暮死,存活的對象少赠尾,可回收對象多力穗。
選用復(fù)制算法,只要付出少量存活對象的復(fù)制成本就可以完成收集气嫁。- 老年代 : 對象存活率高当窗,回收的對象很少。
選用標(biāo)記 - 清理算法寸宵,或者標(biāo)記 - 整理算法來進(jìn)行回收超全。
- 對于新生代采取復(fù)制算法,因為新生代中每次垃圾回收都要回收大部分對象邓馒,也就是說需要復(fù)制的操作次數(shù)較少,采用復(fù)制算法效率最高蛾坯。
- 但實際中并不是按照上面算法中說的1:1的比例來劃分新生代的空間的光酣,而是將新生代劃分為一塊較大的Eden空間和兩塊較小的Survivor空間,比例為8:1:1(比例可以調(diào)整)脉课。
- 每次使用Eden和其中一塊Survivor救军,當(dāng)回收時,將Eden和剛才用過的Survivor空間中還存活的對象一次性的復(fù)制到另一塊Survivor空間上倘零,最后清理掉Eden和剛才用過的Survivor空間唱遭。(后面會繼續(xù)研究,如果這次復(fù)制的對象大小超過Survivor空間呈驶,將會有分配擔(dān)保機(jī)制拷泽。)
- 由于老年代的特點是每次回收都只回收少量對象,使用的是標(biāo)記 - 清理算法,或者標(biāo)記 - 整理算法來進(jìn)行回收司致。它與新生代的比例為 2(老年代) : 1(新生代)拆吆。當(dāng)然這個比例也是可以調(diào)整的。
160f7b66a6600310.png
分代收集算法工作流程
- 1 . 分配了一個又一個對象 : 放到Eden區(qū)脂矫。
- 2 . 不好枣耀,Eden區(qū)滿了,只能GC(新生代GC:Minor GC)了 : 把Eden區(qū)的存活對象copy到Survivor A區(qū)庭再,然后清空Eden區(qū)(本來Survivor B區(qū)也需要清空的捞奕,不過本來就是空的)
- 3 . 又分配了一個又一個對象 : 放到Eden區(qū)。
- 4 . 不好拄轻,Eden區(qū)滿了颅围,只能GC(新生代GC:Minor GC)了 : 把Eden區(qū)和Survivor A區(qū)的存活對象copy到Survivor B區(qū),然后清空Eden區(qū)和Survivor A區(qū)哺眯。
- 5 . 又分配了一個又一個對象 : 放到Eden區(qū)谷浅。
- 6 . 不好,Eden區(qū)滿了奶卓,只能GC(新生代GC:Minor GC)了 : 把Eden區(qū)和Survivor B區(qū)的存活對象copy到Survivor A區(qū)一疯,然后清空Eden區(qū)和Survivor B區(qū)
為什么不是一塊Survivor空間而是兩塊?- 這里涉及到一個新生代和老年代的存活周期的問題夺姑,比如一個對象在新生代經(jīng)歷15次(僅供參考)GC墩邀,就可以移到老年代了。問題來了盏浙,當(dāng)我們第一次GC的時候眉睹,我們可以把Eden區(qū)的存活對象放到Survivor A空間,但是第二次GC的時候废膘,Survivor A空間的存活對象也需要再次用Copying算法竹海,放到Survivor B空間上,而把剛剛的Survivor A空間和Eden空間清除丐黄。第三次GC時斋配,又把Survivor B空間的存活對象復(fù)制到Survivor A空間,如此反復(fù)灌闺。 所以艰争,這里就需要兩塊Survivor空間來回倒騰。
為什么Eden空間這么大而Survivor空間要分的少一點桂对?
- 新創(chuàng)建的對象都是放在Eden空間甩卓,這是很頻繁的,尤其是大量的局部變量產(chǎn)生的臨時對象蕉斜,這些對象絕大部分都應(yīng)該馬上被回收逾柿,能存活下來被轉(zhuǎn)移到survivor空間的往往不多缀棍。所以,設(shè)置較大的Eden空間和較小的Survivor空間是合理的鹿寻,大大提高了內(nèi)存的使用率睦柴,緩解了Copying算法的缺點。
我看8:1:1就挺好的毡熏,當(dāng)然這個比例是可以調(diào)整的坦敌,包括上面的新生代和老年代的1:2的比例也是可以調(diào)整的。
新的問題又來了痢法,從Eden空間往Survivor空間轉(zhuǎn)移的時候Survivor空間不夠了怎么辦狱窘?直接放到老年代去。