Lua 源码学习笔记(4)GC

Lua 源码学习笔记(4)GC

参考书籍:《Lua设计与实现》

作者书籍对应Github:https://github.com/lichuang/Lua-Source-Internal

云风的 BLOG,Lua GC 的工作原理:https://blog.codingnow.com/2018/10/lua_gc.html

云风的 BLOG,Lua GC 的源码剖析 (1):https://blog.codingnow.com/2011/03/lua_gc_1.html

探索Lua5.2内部实现:Garbage Collection(1) 原理:https://blog.csdn.net/yuanlin2008/article/details/8558103

探索Lua5.2内部实现:Garbage Collection(2):https://blog.csdn.net/yuanlin2008/article/details/8684869

深入探究Lua的GC算法(上)-《Lua设计与实现》:https://www.cnblogs.com/zblade/p/8824376.html

  • Lua版本:5.3.5

概述

  • GC一般原理:遍历系统所有对象,没有引用的对象就认为是可以回收的,可以删除。

  • 引用计数算法:对象被引用的时候加1,反之减1,如果引用数量为0,就是没有引用。这个算法优点是不用遍历所有对象,但是有个缺点是,会有循环引用问题。

  • 标记清除算法:首先扫描标记系统中所有对象,被扫描并且标记到的对象认为是可达的(reachable),这些对象不会不会被回收,反之可以。(Lua使用这种)


Lua5.0版本GC算法:双色标记清除法

在这里插入图片描述

  • 缺点:不能被打断,标记阶段和回收阶段必须一起完成。(即如果中间过程加入新对象,标记为白色就会被回收,标记为黑色就还没有被扫描过。)

Lua5.1版本GC算法:三色标记清除法

  1. 白色:待访问状态。表示对象还没有被垃圾回收的标记过程访问到。
  2. 灰色:待扫描状态。表示对象已经被垃圾回收访问到了,但是对象本身对于其他对象的引用还没有进行遍历访问。
  3. 黑色:已扫描状态。表示对象已经被访问到了,并且也已经遍历了对象本身对其他对象的引用。

在这里插入图片描述

每个新创建的对象颜色设置为白色
//初始化阶段
遍历root节点中引用的对象,从白色置为灰色,并且放入到灰色节点列表中
//标记阶段
while(灰色链表中还有未扫描的元素):
	从中取出一个对象,将其置为黑色
    遍历这个对象关联的其他所有对象:
        if 为白色:
        	标记为灰色,加入到灰色链表中(insert to the head)
 
//回收阶段
遍历所有对象:
    if 为白色,
    	没有被引用的对象,执行回收
    else
    	重新塞入到对象链表中,等待下一轮GC
  • 双白色:简单地说, Lua 中的白色分为“当前白色”( currentwhite )和“非当前白色”( otherwhite ) 。这两种白色的状态交替使用。代码在回收时会做判断,如果某个对象的白色不是此次GC 使用的白色状态,那么将不会认为是没有被引用的对象而回收,这样的白色对象将留在下一次GC 中进行扫描。
// lgc.h

/* Layout for bit use in 'marked' field: */
#define WHITE0BIT	0  /* object is white (type 0) */
#define WHITE1BIT	1  /* object is white (type 1) */
#define BLACKBIT	2  /* object is black */
#define FINALIZEDBIT	3  /* object has been marked for finalization */
/* bit 7 is currently used by tests (luaL_checkmemory) */

具体流程

创建对象luaC_newobj

  1. 创建GCObject
  2. 标记白色
  3. 设置类型
  4. 放到GC列表
// lgc.c

/*
** create a new collectable object (with given type and size) and link
** it to 'allgc' list.
*/
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
  global_State *g = G(L);
  GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz));
  o->marked = luaC_white(g);
  o->tt = tt;
  o->next = g->allgc;		// 插在GC列表表头
  g->allgc = o;				/* list of all collectable objects */
  return o;
}

执行单步singlestep

// lgc.h

/*
** Possible states of the Garbage Collector,GC过程中的步骤
*/
#define GCSpropagate	0
#define GCSatomic	1
#define GCSswpallgc	2
#define GCSswpfinobj	3
#define GCSswptobefnz	4
#define GCSswpend	5
#define GCScallfin	6
#define GCSpause	7


static lu_mem singlestep (lua_State *L) {
  global_State *g = G(L);
  switch (g->gcstate) {
    case GCSpause: {			// 一段GC循环的开始
      g->GCmemtrav = g->strt.size * sizeof(GCObject*);
      restartcollection(g);		// 标记为灰色:mark root set and reset all gray lists, to start a new collection
      g->gcstate = GCSpropagate;
      return g->GCmemtrav;
    }
    case GCSpropagate: {		// 传播阶段?
      g->GCmemtrav = 0;
      lua_assert(g->gray);
      propagatemark(g);			// 转换灰成黑(除了线程,一直是灰)
       if (g->gray == NULL)  	// 如果没有灰对象了,就执行下一个阶段
        g->gcstate = GCSatomic;  /* finish propagate phase */
      return g->GCmemtrav;  /* memory traversed in this step */
    }
    case GCSatomic: {
      lu_mem work;
      propagateall(g);  /* make sure gray list is empty */
      work = atomic(L);  /* work is what was traversed by 'atomic' */
      entersweep(L);		// 进入回收阶段
      g->GCestimate = gettotalbytes(g);  /* first estimate */;
      return work;
    }
    case GCSswpallgc: {  /* sweep "regular" objects */
      return sweepstep(L, g, GCSswpfinobj, &g->finobj);
    }
    case GCSswpfinobj: {  /* sweep objects with finalizers */
      return sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
    }
    case GCSswptobefnz: {  /* sweep objects to be finalized */
      return sweepstep(L, g, GCSswpend, NULL);
    }
    case GCSswpend: {  /* finish sweeps */
      makewhite(g, g->mainthread);  /* sweep main thread */
      checkSizes(L, g);				// 有必要时,收缩String表
      g->gcstate = GCScallfin;
      return 0;
    }
    case GCScallfin: {  /* call remaining finalizers */
      if (g->tobefnz && g->gckind != KGC_EMERGENCY) {
        int n = runafewfinalizers(L);		// 针对每个对象调用回收方法
        return (n * GCFINALIZECOST);
      }
      else {  /* emergency mode or no more finalizers */
        g->gcstate = GCSpause;  /* finish collection */
        return 0;
      }
    }
    default: lua_assert(0); return 0;
  }
}



/*
** mark root set and reset all gray lists, to start a new collection
** 标记 mainthread,l_registry,G表 为灰色,最终跑到reallymarkobject
*/
static void restartcollection (global_State *g) {
  g->gray = g->grayagain = NULL;
  g->weak = g->allweak = g->ephemeron = NULL;
  markobject(g, g->mainthread);
  markvalue(g, &g->l_registry);
  markmt(g);
  markbeingfnz(g);  /* mark any finalizing object left from previous cycle */
}

barrier操作

  • 向前走一步:如果一个新创建对象的颜色是白色,而它被一个黑色对象引用了,那么将这个对象的颜色从白色变成灰色。
    • 当前GC没有在扫描阶段标记,就标记这个对象,否则标记白色,等下次扫描。
  • 向后走一步:黑色的对象回退到灰色,也就是这个原先已经被标记为黑色的对象需要重新被扫描。
    • 对于表:只要有对象新增到Table,就回退到灰色重新扫。

/*
** barrier that moves collector forward, that is, mark the white object
** being pointed by a black object. (If in sweep phase, clear the black
** object to white [sweep it] to avoid other barrier calls for this
** same object.)
*/
void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
  global_State *g = G(L);
  lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
  if (keepinvariant(g))  /* must keep invariant? */
    reallymarkobject(g, v);  /* restore invariant */
  else {  /* sweep phase */
    lua_assert(issweepphase(g));
    makewhite(g, o);  /* mark main obj. as white to avoid other barriers */
  }
}


/*
** barrier that moves collector backward, that is, mark the black object
** pointing to a white object as gray again.
*/
void luaC_barrierback_ (lua_State *L, Table *t) {
  global_State *g = G(L);
  lua_assert(isblack(t) && !isdead(g, t));
  black2gray(t);  /* make table gray (again) */
  linkgclist(t, g->grayagain);
}

进度控制

  • 两种方式回收:
    • 自动回收:GCdebt 大于0。
    • 用户调API

/*
** Does one step of collection when debt becomes positive. 'pre'/'pos'
** allows some adjustments to be done only when needed. macro
** 'condchangemem' is used only for heavy tests (forcing a full
** GC cycle on every opportunity)
*/
#define luaC_condGC(L,pre,pos) \
	{ if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
	  condchangemem(L,pre,pos); }

/* more often than not, 'pre'/'pos' are empty */
#define luaC_checkGC(L)		luaC_condGC(L,(void)0,(void)0)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值