Lua中的GC

原文地址:https://www.cnblogs.com/zblade/p/8824376.html

原文地址:https://blog.csdn.net/BigBrick/article/details/85317491

原文地址:https://blog.codingnow.com/2018/10/lua_gc.html

一、GC的原理及其算法设计

不同的语言,对GC算法的设计不同,常见的GC算法是引用计数和Mark-Sweep算法, c#采用的是Mark-sweep && compact算法, Lua采用的是Mark-sweep算法,分开说一下:

引用计数算法:在一个对象被引用的情况下,将其引用计数加1,反之则减1,如果计数值为0,则在GC的时候回收,这个算法有个问题就是循环引用。

Mark-sweep算法(标记清除法):每次GC的时候,对所有对象进行一次扫描,如果该对象不存在引用,则被回收,反之则保存。

在Lua5.0及其更早的版本中,Lua的GC是一次性不可被打断的过程,使用的Mark算法是双色标记算法(Two color mark),这样系统中对象的非黑即白,要么被引用,要么不被引用,这会带来一个问题:在GC的过程中如果新加入对象,这时候新加入的对象无论怎么设置都会带来问题,如果设置为白色,则如果处于回收阶段,则该对象会在没有遍历其关联对象的情况下被回收;如果标记为黑色,那么没有被扫描就被标记为不可回收,是不正确的。

为了降低一次性回收带来的性能问题以及双色算法的问题,在Lua5.1后,Lua都采用分布回收以及三色增量标记清除算法(Tri-color incremental mark and sweep)

白色:可回收状态。
详解:如果该对象未被GC标记过则此时白色代表当前对象为待访问状态。举例:新创建的对象的初始状态就应该被设定为白色,因为该对象还没有被GC标记到,所以保持初始状态颜色不变,仍然为白色。如果该对象在GC标记阶段结束后,仍然为白色则此时白色代表当前对象为可回收状态。但其实本质上白色的设定就是为了标识可回收。
灰色:中间状态。
详解:当前对象为待标记状态。举例:当前对象已经被GC访问过,但是该对象引用的其他对象还没有被标记(新创建的引用)。
黑色:不可回收状态。
详解:当前对象为已标记状态。举例:当前对象已经被GC访问过,并且对象引用的其他对象也被标记了。
备注:白色分为白1和白2。原因:在GC标记阶段结束而清除阶段尚未开始时,如果新建一个对象,由于其未被发现引用关系,原则上应该被标记为白色,于是之后的清除阶段就会按照白色被清除的规则将新建的对象清除。这是不合理的。于是lua用两种白色进行标识,如果发生上述情况,lua依然会将新建对象标识为白色,不过是“当前白”(比如白1)。而lua在清扫阶段只会清扫“旧白”(比如白2),在清扫结束之后,则会更新“当前白”,即将白2作为当前白。下一轮GC将会清扫作为“旧白”的白1标识对象

--标记清除法分为四个阶段
(1)标记阶段:把根节点的集合(由lua语言可以直接访问的对象组成)标记为活跃状态,在lua语言中,这个集合值包括注册表,保存在一个活跃对象中的对象是程序可达的,因此也会被标记为活跃(弱引用表中的内容除外)当所有可达对象都被标记为活跃后
标记阶段结束。
(2)清理阶段:首先,lua语言会遍历所有的被标记为需要进行析构,但是又没有被标记为活跃状态的对象。这些没有被标记为活跃状态的对象会被标记为活跃(复苏),并且被放在一个单独的列表中,这个列表会在析构阶段用到,然后,lua语言遍历弱引用表并从中移除键或值未被标记的元素。

  • 在 Lua 5.0 以前,Lua 使用的是一个非常简单的标记扫描算法。它从根集开始遍历对象,把能遍历到的对象标记为活对象;然后再遍历通过分配器分配出来的对象全集链表,把没有标记为活对象的其它对象都删除。

    但是,Lua 5.0 支持 userdata ,它可以有 __gc 方法,当 userdata 被回收时,会调用这个方法。所以,一遍标记是不够的,不能简单的把死掉的 userdata 简单剔除,那样就无法正确的调用 __gc 了。所以标记流程需要分两个阶段做,第一阶段把包括 userdata 在内的死对象剔除出去,然后在死对象中找回有 __gc 方法的,对它们再做一次标记复活相关的对象,这样才能保证 userdata 的 __gc 可以正确运行。执行完 __gc 的 userdata 最终会在下一轮 gc 中释放(如果没有在 __gc 中复活)。 userdata 有一个单向标记,标记 __gc 方法是否有运行过,这可以保证 userdata 的 __gc 只会执行一次,即使在 __gc 中复活(重新被根集引用),也不会再次分离出来反复运行 finalizer(终结器) 。也就是说,运行过 finalizer 的 userdata 就永久变成了一个没有 finalizer 的 userdata 了。

(3)清除阶段:遍历所有的对象(lua会把所有创建的对象放在一个链表中),如果一个对象没有被标记为活跃状态,就将其回收,否则,就清理标记,然后准备进入下一个清理周期。
(4)析构阶段:调用清理阶段被分离出来的对象的析构器(我的理解就是拿到清理阶段的列表,清理列表中的对象,列表只包含真正需要清理的对象,那些在弱引用表中被标记的元素已经从列表中移除,也就是有__gc 方法的对象)。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值