分代垃圾回收,基于的是“大部分的對象,在生成后馬上就會變成垃圾”這一經(jīng)驗(yàn)上的事實(shí)為設(shè)計(jì)出發(fā)點(diǎn)。此前討論過基于引事實(shí)的另一個垃圾回收算法,引用計(jì)數(shù)出的一些優(yōu)化思路。
分代的關(guān)鍵是:
給對象記錄下一個age,隨著每一次垃圾回收,這個age會增加;
給不同age的對象分配不同的堆內(nèi)內(nèi)存空間,稱為某一代;
對某一代的空間,有適合其的垃圾回收算法;
對每代進(jìn)行不同垃圾回收,一般會需要一個額外的信息:即每代中對象被其他代中對象引用的信息。這個引用信息對于當(dāng)前代來說,扮演與"root"一樣的角色,也是本代垃圾回收的起點(diǎn)。
分代垃圾回收的典型是Ungar的分代垃圾回收。
它將堆分成如下形式:
如上,分成新生代與老年代。
在新生代內(nèi),又分成了生成空間與幸存空間。當(dāng)生成空間滿了,會以復(fù)制算法進(jìn)行垃圾回收,復(fù)制到幸存空間中。和前面的復(fù)制算法匹配,幸存空間又一分為二,分成from和to空間。每次新生代的垃圾回收,會同時進(jìn)行生成空間到to、from空間到to的兩個垃圾回收。
對于老年代,則直接進(jìn)行mark_sweep回收。
對于“記錄集”(record set),是記錄代間引用的一個數(shù)組。它內(nèi)部不能只記錄被引用對象,因?yàn)楸灰脤ο蟊粡?fù)制到to空間后,引用者本身的引用指針要更新,只記錄被引用的新生代內(nèi)對象是無法找到被引用者的。所以,必須在記錄集中記錄老年代內(nèi)對象。
更新記錄集的操作在分配新對象,并設(shè)置成老對象的一個field時進(jìn)行:
write_barrier(obj, field, new_obj) { if obj >= $old_start && new_obj < $old_start && obj.remembered == false // 條件,很明顯 $rs[$rs_idx++] = obj // 更新記錄集