Lua的GC回收机制

这篇博客详细介绍了Lua的垃圾回收机制,包括GC的原理、数据结构、Lua5.1后采用的三色增量标记清除算法以及GC的具体过程,如新建对象阶段、触发条件、标记和清除阶段。博客探讨了不同阶段的源码实现,强调了三色标记如何提高系统实时性,并分析了GC过程中的关键函数和状态机。
摘要由CSDN通过智能技术生成

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时,先以若干根节点开始,逐个把直接或间接和它们相关的节点都做上标记;
清除:当标记完成后,遍历整个对象链表,把被标记为需要删除的节点一一删除即可。

其基本的原理伪代码,参考书中原文为:

每个新创建的对象颜色设置为白色

//初始化阶段

遍历root节点中引用的对象,从白色置为灰色,并且放入到灰色节点列表中

//标记阶段

while(灰色链表中还有未扫描的元素):

从中取出一个对象,将其置为黑色

遍历这个对象关联的其他所有对象:

if 为白色

标记为灰色,加入到灰色链表中(insert to the head)

//回收阶段

遍历所有对象:

if 为白色,

没有被引用的对象,执行回收

else

重新塞入到对象链表中,等待下一轮GC

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

GC的数据结构

分析Lua中对于需要GC的类型数据

#define iscollectable(o) (ttype(o) >= LUA_TSTRING)

都会有一个基本的定义CommonHeader,其定义为:
在这里插入图片描述
next: GCObject链表指针,该指针用来将所有的GC对象都链接在一个表中;

tt: 数据类型:nil, boolean, number, string…

marked: 标记字段,byte表示的字段颜色定义为在这里插入图片描述

这儿特定解释一下为什么会有两种白色,前面提到,5.1后的Lua采用的是三色标记算法,其实质是四色标记算法,分为0型白色和1型白色,在GC回收的时候,会设置当前的白色为其中一种,详见globalstate中的currentwhite,这样在代码回收的时候,如果当前对象的白色不为currentwhite,则认为其不可回收,这样的对象需要等到下一次的GC才能决定是否回收,具体参看后面的,会有对应的应用。对于global_state的设计为:
在这里插入图片描述

Lua垃圾回收详细过程

在这里插入图片描述

步骤源码详解

1.新建对象阶段

STEP1:新建可回收对象,将其颜色标记为白色
解释:首先lua对数据类型进行了抽象,具体数据结构可见如下源码:
lobject.h

** Common type for all collectable objects
*/
typedef struct GCObject GCObject;

/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader  GCObject *next; lu_byte tt; lu_byte marked

/*
** Common type has only the common header
*/
struct GCObject {
   
  CommonHeader;
};


/*
** Tagged Values. This is the basic representation of values in Lua,
** an actual value plus a tag with its type.
*/
/*
** Union of all Lua values
*/
typedef union Value {
   
  GCObject *gc;    /* collectable objects */
  void *p;         /* light userdata */
  int b;           /* booleans */
  lua_CFunction f; /* light C functions */
  lua_Integer i;   /* integer numbers */
  lua_Number n;    /* float numbers */
} Value;

#define TValuefields  Value value_; int tt_

typedef struct lua_TValue {
   
  TValuefields;
} TValue;

由代码分析可知,Lua的实现中,数据类型可以分为需要被GC管理回收的对象、按值存储的对象。而STEP1中提及的“可回收对象”(GCObject)就是指那些需要被GC管理回收的对象,在lua5.3中,具体是指:TString、Udata、Cloure、Table、Proto、lua_State(可通过查看GCUnion)这些数据类型。这些数据类型都有一个共同的部分CommonHeader:
lobject.h

#define CommonHeader
GCObject *next; lu_byte tt; lu_byte marked

其中next链接下一个GCObject,tt表明数据类型,marked用于存储之前提到的颜色。
创建上述数据类型对象是通过lua虚拟机调用对应的数据类型创建函数完成的。在创建过程中,总会调用luaC_newobj这个函数,来完成GCObject的初始化。
以Table为例,会依次调用:
(lvm.c)[luaV_execute]->(ltable.c)[luaH_new]->(lgc.c)[luaC_newobj]
下面我们来看luaC_newobj:
lgc.c

/*
** create a new collectable object (with given type and size) and link
** it to 'allgc' list.
*/
GCObject 
Lua 中,垃圾收集(Garbage Collection, GC)是一个自动内存管理机制,用于释放不再使用的内存。Lua 的默认 GC 会跟踪值类型的引用,当没有其他变量引用某个对象时,该对象会被回收LuaJIT 和 Xlua 这两个库对 LuaGC 特性提供了不同的优化和扩展: 1. LuaJIT:它是 Lua 的一个高性能版本,其 GC 实现更为高效。LuaJIT 使用了一个增量式 GC,这意味着它可以边执行代码边回收内存,而且它的“轻量级”线程模型也减少了 GC 对性能的影响。LuaJIT 另外还支持一种名为“惰性 GC”的模式,可以在脚本执行期间推迟垃圾收集直到必要时才进行。 2. Xlua:这是一个基于 LuaJIT 的扩展库,它提供了一种更高级别的接口来简化 C/C++ 与 Lua 的交互。Xlua 在一定程度上可以帮助管理和优化内存分配,比如它可能会有自己的内存池或者缓存策略来减少 GC 的频率。同时,Xlua 也可能提供了额外的功能来更好地控制 GC 的行为,比如显式的对象标记或资源清理。 关于如何使用这些库来处理值类型 GC 或内存管理,通常的做法包括: - 在需要时手动释放 Xlua 提供的内存资源。 - 使用 Xlua 的 API 标记特定对象为不被 GC 回收,或者在适当的时间触发 GC。 - 如果 LuaJIT 被选为底层引擎,可能需要了解它的 GC 设置,并调整以适应特定应用的需求,例如设置延迟 GC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值