標(biāo)記-清除算法
最基礎(chǔ)的收集算法是“標(biāo)記-清除”(Mark-Sweep)算法,如同它的名字一樣,算法分為“標(biāo)記”和“清除”兩個階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象,它的標(biāo)記過程其實在前一節(jié)講述對象標(biāo)記判定時已經(jīng)介紹過了。之所以說它是最基礎(chǔ)的收集算法,是因為后續(xù)的收集算法都是基于這種思路并對其不足進(jìn)行改進(jìn)而得到的。它的主要不足有兩個:一個是效率問題,標(biāo)記和清除兩個過程的效率都不高;另一個是空間問題,標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致以后在程序運行過程中需要分配較大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作。
復(fù)制算法
為了解決效率問題,一種稱為“復(fù)制”(Copying)的收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊赶站。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉营搅。這樣使得每次都是對整個半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動堆頂指針,按順序分配內(nèi)存即可,實現(xiàn)簡單,運行高效箕别。只是這種算法的代價是將內(nèi)存縮小為了原來的一半,未免太高了一點测萎。
標(biāo)記-整理算法
復(fù)制收集算法在對象存活率較高時就要進(jìn)行較多的復(fù)制操作,效率將會變低。更關(guān)鍵的是,如果不想浪費50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對被使用的內(nèi)存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法疚顷。根據(jù)老年代的特點,有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內(nèi)存。
分代收集算法
當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用“分代收集”(Generational Collection)算法,這種算法并沒有什么新的思想,只是根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊禁偎。一般是把Java堆分為新生代和老年代,這樣就可以根據(jù)各個年代的特點采用最適當(dāng)?shù)氖占惴ㄍ鹊獭T谛律?每次垃圾收集時都發(fā)現(xiàn)有大批對象死去,只有少量存活,那就選用復(fù)制算法,只需要付出少量存活對象的復(fù)制成本就可以完成收集。而老年代中因為對象存活率高如暖、沒有額外空間對它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記—清理”或者“標(biāo)記—整”算法來進(jìn)行回收笆檀。