垃圾回收算法详解:分代、增量与Baker算法
1. 分代垃圾回收
在许多程序中,新创建的对象很可能很快就会死亡,而在多次垃圾回收后仍然可达的对象可能会在更多次回收中存活下来。因此,垃圾回收器应将精力集中在“年轻”的数据上,因为这些数据中垃圾的比例更高。
1.1 堆的分代
我们将堆划分为不同的代,最年轻的对象位于G0代;G1代中的每个对象都比G0代中的任何对象老;G2代中的所有对象都比G1代中的任何对象老,依此类推。
1.2 回收G0代
要回收G0代(通过标记 - 清除或复制算法),只需从根开始进行深度优先标记或广度优先复制(或半深度优先复制)。但此时根不仅包括程序变量,还包括G1、G2等代中指向G0代的任何指针。
幸运的是,较老的对象指向较年轻的对象的情况很少见。在许多常见的编程风格中,当创建对象a时,其字段会立即初始化,例如指向已经存在的对象b和c,因此通常是较新的对象指向较老的对象。较老的对象b指向较新的对象a的唯一方式是在b创建很久之后更新其某个字段,而这种情况很少发生。
为了避免在G1、G2等代中搜索G0代的根,我们让编译后的程序记录从老对象到新对象的指针位置。有以下几种记录方式:
- 记录列表 :编译器在每次执行形如b.fi ← a的更新存储操作后,生成代码将b放入一个更新对象的向量中。然后,在每次垃圾回收时,回收器扫描该记录列表,查找指向G0代的老对象b。
- 记录集 :与记录列表类似,但使用对象b中的一位来记录b已经在向量中。这样,编译器生成的代码可以检查该位,避免向量中对b的重复引用