分代垃圾回收
- 理论支持:经验得出——"大部分的对象在生成后马上就变成了垃圾,很少有对象能活得很久"。
- 分代垃圾回收将刚生成的对象称为新生代,达到一定年龄(进过一次GC即一岁)的对象称为老年代,不同代的对象使用不同回收算法。
- 新生代对象执行GC称为新生代GC(minor GC)。
- 新生代对象存活一定次数GC将晋升到老年代,老年代的GC称为老年代GC(major GC)。
Ungar的分代垃圾回收
准备
- 堆分为生成空间(分配内存的位置)、2个大小相等的幸存空间(From和To)以及老年代空间。
- 记录集用于记录老年代指向新生代的引用,为了便于高效的寻找从老年代到新生代的引用。
- 记录集记录的是老年代中发出引用的对象
- 记录集通过写入屏障在mutator更新对象间的指针操作进行写入。
- 对象需要增加三个字段:
- age:对象年龄
- forwarded:已经复制完毕的标志
- remembered:已经向记录集记录完毕的标志
分配
- 分配是在生成空间进行,其分配算法与复制算法基本一致。
- 当空间不够将发起minor GC,如果还不足则表示生成空间比新对象小,这种有些直接写入老年代。
新生代GC
- minor GC把生成空间和From中的对象复制到To或老年代,具体复制目标由对象年龄决定。
- 当对象需要晋升到老年代时,如果老年代空间不够将发起一次major GC。
- 晋升时不仅要设置forwarded和forwaring,如果该对象还有引用了新生代的对象则需要写入到记录集。
- minor GC的根除了正常的GC root还有记录集中的对象。
老年代GC
老年代GC使用Mark-Sweep算法。
总结
- 改善了GC所花费的时间,提高了吞吐量。
- 其针对的是“很对年轻代对象很快就死了”,如果不符合这个特点可能造成相反的结果。
记录各代之间引用的方法
卡片标记
- 将老年代按照一定大小分割,分割后的空间称为卡片,每个卡片准备一个标志位。
- 优点在于节约空间而且不会像记录集那样可能会溢出。
- 缺点在于搜索标志位。
页面标记
- 许多OS以页面对内存进行管理,将卡片标记法的卡片大小设为页的大小。
- 当某个页进行写入操作时,设置该页的重写标志位,从而达到标记。
- 缺点:并不是所有OS都支持页的模式。
改进
多代垃圾回收
- 增加代数,给每代记录一个记录集。
- 并不是代数越多越好,需要根据实际情况判断。
列车垃圾回收
- 目的在于控制老年代GC的最大停顿时间。
- 堆划分为一个个车厢,1个以上的车厢连接构成列车,1次老年代GC是以1个车厢作为GC对象。
- 每个车厢和每个列车均有一个对应的记录集,记录来自其他车厢或列车的引用。
- 新生代GC将GC root和记录集中引用的对象复制到老年代,其中GC root引用相关对象将复制到一个空闲车厢,而记录集中的对象则复制到老年代发起引用的车厢对应列车的尾车厢中。
- 老年代GC以列车开头车厢作为GC对象,将该车厢对象复制与年轻代复制的目标选择一样。
- 写入屏障,老年代指向新生代则写入新生代记录集,老年代指向老年代则如果指针目标对象所在的车厢在指针对象所做车厢之前需要记录到指针目标对象的记录集中(原因在于按顺序回收车厢)