前言
在业务中使用Lua时,会有一些配置表,配置表的内容会越来越大,这些配置表一般在进程启动后不会被回收,生命周期伴随着进程的启停,但是这些表会参与GC,如果能让这些表不参与GC,那么可以减少一次GC的消耗。
在此情况下,想要实现让一个Lua表不参与GC,且可选为只读。
lua gc
在lua gc中想要将一个table从gc的各阶段中去除,我们需要做两件事
- 在gray遍历到该table时跳过,不遍历此table下的所有字段
- 在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后效果明显
- 在正式项目,对一个物品配置表调用nogc耗时,大概2ms