在lgc.h文件中, 定义了以下几个宏, 是lua gc过程中的一些状态
1 | #define GCSpropagate 0 // 标记阶段 |
在global_State中, 定义了一些与GC相关的变量
- GCdebt 表示触发GC的债务, 每当新建对象分配内存时就会累加到g->GCdebt, 达到临界值时触发GC
- allgc 所有未被标记为终结的对象
- finobj 所有被标记为终结的对象
- tobefnz 所有准备终结的对象(准备调用__gc的对象)
- fixedgc 不会被回收的对象
- gray 灰色对象列表
- grayagain 需要进行原子操作的对象列表
- gcstate 当前状态
Lua内存回收使用的是三色法
白色:对象没有被垃圾回收器访问时候标记为白色
灰色:这个对象正处在被扫描,但是它所有引用都没访问完的时候被标记为灰色
黑色:当一个对象的所有引用都被扫描了以后的时候,这个对象被标记为黑色
在清除阶段, 会将所有白色对象回收
在luaD_precall
luaD_poscall
lua_newuserdata
lua_concat
lua_createtable
等函数中, 会调用luaC_checkGC
, 在这个函数中, 会去检查GCdebt
, 达到内存回收条件, 就会调用luaC_step
进行内存回收工作
gcstate 初始为GCSpause状态, 会调用函数
restartcollection(global_State)
将mainthread l_registry
以及相关元表标记为灰色1
2
3
4
5
6
7
8static 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 */
}进入标记阶段, 标记阶段分为GCSpropagate 和 GCSatomic
GCSpropagate: 从gray中不停的取出对象, 将其标记为黑色, 这一操作是分步进行的, 而且, 对象会根据对象类型的不同进行不同的处理
1
2
3
4
5
6
7
8gray2black(o);
switch (o->tt) {
case LUA_TTABLE:如果是弱表, 就回退到**灰色**, 否则遍历标记数组与哈希部分
case LUA_TLCL: 对**upval**进行标记
case LUA_TCCL: 对**upval**进行标记
case LUA_TTHREAD:移动到**grayagain**, 进行原子操作处理
case LUA_TPROTO:对**字符串** **upvalue** **局部变量**进行遍历标记
}GCSatomic 除了处理 gray grayagain 等, 还会扫描finobj将可回收对象转移到tobefnz, 以及弱表
1
2
3propagateall(g); /* make sure gray list is empty */
work = atomic(L); /* work is what was traversed by 'atomic' */
entersweep(L);GCSswpallgc 清理allgc链表内的对象
sweepstep(L, g, GCSswpfinobj, &g->finobj);
GCSswpfinobj 清理finobj链表内的对象
sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
GCSswptobefnz 清理tobefnz链表内的对象
sweepstep(L, g, GCSswpend, NULL);
GCSswpend 收尾工作, 标记
mainthread上
为白色, 检查内存释放mainthread上的一些空间, 如:字符串表,连接缓冲区等1
2makewhite(g, g->mainthread); /* sweep main thread */
checkSizes(L, g);GCScallfin 取出tobefnz链表中的对象, 调用其
__gc
, 然后放入到allgc中1
2
3
4
5
6
7
8
9
10static int runafewfinalizers (lua_State *L) {
global_State *g = G(L);
unsigned int i;
lua_assert(!g->tobefnz || g->gcfinnum > 0);
for (i = 0; g->tobefnz && i < g->gcfinnum; i++)
GCTM(L, 1); /* call one finalizer */
g->gcfinnum = (!g->tobefnz) ? 0 /* nothing more to finalize? */
: g->gcfinnum * 2; /* else call a few more next time */
return i;
}