Lua设置table不gc

218 阅读2分钟

前言

  在业务中使用Lua时,会有一些配置表,配置表的内容会越来越大,这些配置表一般在进程启动后不会被回收,生命周期伴随着进程的启停,但是这些表会参与GC,如果能让这些表不参与GC,那么可以减少一次GC的消耗。

  在此情况下,想要实现让一个Lua表不参与GC,且可选为只读。

lua gc

在lua gc中想要将一个table从gc的各阶段中去除,我们需要做两件事

  1. 在gray遍历到该table时跳过,不遍历此table下的所有字段
  2. 在rootgc链中将table和其引用的所有对象从gc链中去除,防止被gc遍历后回收

同时我希望能尽量少的侵入源码

第一点 在gray遍历到该table时跳过,不遍历此table下的所有字段

  看gc源码中,在遍历table时会判断是否为弱表,那这个就可以再此利用,通过自定义字段,我们可以在traversetable中跳过遍历该table

/* 添加了一个skiptable的判断 */
static lu_mem traversetable (global_State *g, Table *h) {
  const char *weakkey, *weakvalue, *skiptable;
  const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
  markobjectN(g, h->metatable);
  if (mode && ttisstring(mode) &&  /* is there a weak mode? */
      (cast_void(weakkey = strchr(svalue(mode), 'k')),
       cast_void(weakvalue = strchr(svalue(mode), 'v')),
       cast_void(skiptable = strchr(svalue(mode), 's')),
       (weakkey || weakvalue || skiptable))) {  /* is really weak? */ /* is skip table? */
    if (skiptable) goto END;
    if (!weakkey)  /* strong keys? */
      traverseweakvalue(g, h);
    else if (!weakvalue)  /* strong values? */
      traverseephemeron(g, h, 0);
    else  /* all weak */
      linkgclist(h, g->allweak);  /* nothing to traverse now */
  }
  else  /* not weak */
    traversestrongtable(g, h);
  END:
  return 1 + h->alimit + 2 * allocsizenode(h);
}

第二点 在rootgc链中将table和其引用的所有对象从gc链中去除,防止被gc遍历后回收

  这个就相对简单,我们获取到rootgc的链表,在5.4.4中可以通过global state获得

global_State* g = G(L);
GCObject* prep = nullptr, * p = g->allgc;

  通过遍历table,层层收集table引用的对象,然后从rootgc中去除

测试

  • 一个table,包含一个function,以及数组长度5万,每个包含一个数字和字符串,hashkey个数5万,每个包含一个数字和字符串,调用nogc测试结果
  • 可以发现nogc后效果明显

image-1.png

  • 在正式项目,对一个物品配置表调用nogc耗时,大概2ms

image-2.png

完整代码 git仓库

github.com/CNicer/Lua-…