分代垃圾回收,基于的是“大部分的对象,在生成后马上就会变成垃圾”这一经验上的事实为设计出发点。此前讨论过基于引事实的另一个垃圾回收算法,引用计数出的一些优化思路。
分代的关键是:
给对象记录下一个age,随着每一次垃圾回收,这个age会增加;
给不同age的对象分配不同的堆内内存空间,称为某一代;
对某一代的空间,有适合其的垃圾回收算法;
对每代进行不同垃圾回收,一般会需要一个额外的信息:即每代中对象被其他代中对象引用的信息。这个引用信息对于当前代来说,扮演与"root"一样的角色,也是本代垃圾回收的起点。
分代垃圾回收的典型是Ungar的分代垃圾回收。
它将堆分成如下形式:
如上,分成新生代与老年代。
在新生代内,又分成了生成空间与幸存空间。当生成空间满了,会以复制算法进行垃圾回收,复制到幸存空间中。和前面的复制算法匹配,幸存空间又一分为二,分成from和to空间。每次新生代的垃圾回收,会同时进行生成空间到to、from空间到to的两个垃圾回收。
对于老年代,则直接进行mark_sweep回收。
对于“记录集”(record set),是记录代间引用的一个数组。它内部不能只记录被引用对象,因为被引用对象被复制到to空间后,引用者本身的引用指针要更新,只记录被引用的新生代内对象是无法找到被引用者的。所以,必须在记录集中记录老年代内对象。
更新记录集的操作在分配新对象,并设置成老对象的一个field时进行:
write_barrier(obj, field, new_obj) { if obj >= $old_start && new_obj < $old_start && obj.remembered == false // 条件,很明显 $rs[$rs_idx++] = obj // 更新记录集