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