近期在改 Lua 5.4 的垃圾回收,虽然之前也写过分代垃圾回收的原理,但这次改完之后对其更有感悟,就简单记录一下Lua 5.4 的分代垃圾回收的实现原理。
简介
分代垃圾回收认为对象分为年轻代和老年代,其中年轻代对象很快就会被释放(比如临时对象),而老年代对象存在的时间比较长,不容易被释放,因此也就不需要经常去扫描老年代,只需要经常去扫描年轻代,等到年轻代垃圾回收的时候实在收不回对象,再进行一次全量垃圾回收。
原理
Lua 的 age 总共占用 3 Bit,刚创建出来的对象为 G_NEW
,当它活过一轮垃圾回收后,提升为 G_SURVIVAL
,若再活过一轮垃圾回收,则彻底进入 G_OLD
老年代,不在年轻代中扫描它。
1 |
这里面的 G_OLD0
是用于 Barrier forward,假设你创建了一个新对象,它本该是 G_NEW
但因为它被老年代对象引用,所以必须要强行将它改为老年代,否则会发生跨代引用,该新对象直接被清理掉。
同理 G_TOUCHED1
则是用于 Barrier back,假设你创建了一个新对象,然后放置在一个老年代的 table中,此时为了不频繁触发该 table 的 barrier,则将其修改为 G_TOUCHED1
,同时将其放置在 grayagain
链表中,这是因为老年代table是不会在年轻代的垃圾回收中被扫描到,但此时老年代又确实引用了年轻代对象,所以要将它放在一条特殊链表中,使其能在年轻代中被扫描到。