一.变量和值
Lua是一种 动态类型语言。这意味着变量时没有类型的 只有值有类型
在lua中的值类型一共有八类,number,bool,string,function,table,userdata,thread,nil.其中nil比较特殊,只有一个值就是nil.在lua中变量的"类型"是可以转换的.对于可垃圾回收的gc对象,采用的是都是引用指向的方式,不存在写时拷贝的问题.指向相同数据对象的变量会存在写入覆盖的问题.
GC不回收的有number,bool,和lightuserdata,这些类型都不引起额外的内存分配.
lua中用于可回收值的头部
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/* ** Common header in struct form */ typedef struct GCheader { CommonHeader; } GCheader; next指针用于连接gc链表 tt用于表示gc值的类型 marked用于lua的垃圾回收算法
lua中的值表示 typedef union { GCObject *gc; void *p;
lua_Number n; int b; } Value; #define TValuefields Value value; int tt
typedef struct lua_TValue { TValuefields; } TValue;
所有的变量在c层面上都是TValue类型的 具体的lua变量"类型"则取决于他所使用的Value域 即取决它所指向的值类型 tt用来标示当前的lua变量"类型" 不同类型变量去使用不同域
n用于number类型 b用于boolean p用于lightuserdata(不能回收) 当使用其他各类型时 则需要GCObject*指针 指向对应的值
union GCObject { GCheader gch; union TString ts; union Udata u; union Closure cl; struct Table h; struct Proto p; struct UpVal uv; struct lua_State th; /* thread */ };
变量与值的关系
lua中的变量是没有类型的 只有值有类型 当变量表示number ,bool,lightuserdata等简单值的时候 值类型就反应在Value上 当变量指向其他类型的时候 值类型反应在GCObject指针的指向上面 也就是两个变量可以指向同一个GCObject对象 这样的GCObject就只存在一份了 换句话说就是 变量都是对GC对象的一种引用
变量与值的产生
对于number,bool,lightuserdata这样的简单值来说 变量与值是同时产生的 而对于指向一个GC对象的变量来说 首先应该创建这样的GC对象 然后再将Value对象的GC指针指向这个GC对象 对于一个普通变量的赋值来说 Value只用直接改写相应的域即可 而对于一个GC对象 两个变量会同时指向一个GCObject
字符串对象的产生
lua_pushstring 创建一个TString 并将lua栈顶的TValue指向这个TString
LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { lua_lock(L); luaC_checkGC(L); setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); api_incr_top(L); lua_unlock(L); }
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { GCObject *o; unsigned int h = cast(unsigned int, l); /* seed */ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ size_t l1; for (l1=l; l1>=step; l1-=step) /* compute hash */ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
//lua中所有的字符串变量都放在globalstate中的字符串表中 所以先查找字符串是否已经存在了 for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; o != NULL; o = o->gch.next)
{ TString *ts = rawgco2ts(o); if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) //字符串已经存在
{
if (isdead(G(L), o)) //是否已经死亡 这一步的GC还没有执行GCSweepString 但是已经执行了GCSpropagate 所以才会变成死的 也就是mark还是old white
changewhite(o); //将其复生 重新将这个GCObject* 变成current white就可 return ts; } } return newlstr(L, str, l, h); /* not found */ }
//没有找到字符串 重新创建一个新的
static TString *newlstr (lua_State *L, const char *str, size_t l, unsigned int h)
{ TString *ts; stringtable *tb; if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) luaM_toobig(L); ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); ts->tsv.len = l; ts->tsv.hash = h; ts->tsv.marked = luaC_white(G(L)); //这一步很关键 标记白 表示当前的字符串是活着的 ts->tsv.tt = LUA_TSTRING; ts->tsv.reserved = 0;
//真正的字符串的内容是放在TString变量的后面的 在TString+1的位置上 memcpy(ts+1, str, l*sizeof(char)); ((char *)(ts+1))[l] = '\0'; /* ending 0 */ tb = &G(L)->strt; h = lmod(h, tb->size); ts->tsv.next = tb->hash[h]; /* chain new entry */ tb->hash[h] = obj2gco(ts); //将穿件出来的TString放入GlobalState的全局字符串表中 tb->nuse++; if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) luaS_resize(L, tb->size*2); /* too crowded */ return ts; }
字符串的管理是与其他的GCObject分开的 出于对字符串这种易重用资源的良好管理 提高重用性吗?????
Userdata的创建
LUA_API void *lua_newuserdata (lua_State *L, size_t size) { Udata *u; lua_lock(L); luaC_checkGC(L); //如果满足GC运行的条件 会做一下单步的GC Singlestep 但是只做一步 不会完成GC全过程 u = luaS_newudata(L, size, getcurrenv(L)); setuvalue(L, L->top, u); //创建一个Userdata的GCObject 同时用栈顶的Value指向这个GCObject* api_incr_top(L); lua_unlock(L); return u + 1; }
Udata *luaS_newudata (lua_State *L, size_t s, Table *e)
{ Udata *u; if (s > MAX_SIZET - sizeof(Udata)) luaM_toobig(L); u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); //和TString一样 真正的用户数据是跟在Udata后面的 u->uv.marked = luaC_white(G(L)); /* is not finalized */ 标记白色 u->uv.tt = LUA_TUSERDATA; u->uv.len = s; u->uv.metatable = NULL; u->uv.env = e; /* chain it on udata list (after main thread) */ u->uv.next = G(L)->mainthread->next; G(L)->mainthread->next = obj2gco(u); return u; }
Userdata 放在main LuaState的next中 特殊??? Userdata的管理比较特殊 Userdata不用显示地使用LuaC_link链入GlobalState的rootgc 因为他被链入了mainthread的next中所以也就隐式的被连接入了
rootgc中了
Table创建
LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { lua_lock(L); luaC_checkGC(L); sethvalue(L, L->top, luaH_new(L, narray, nrec)); 将栈顶的value指向这个table api_incr_top(L); lua_unlock(L); }
创建新表
Table *luaH_new (lua_State *L, int narray, int nhash) { Table *t = luaM_new(L, Table); luaC_link(L, obj2gco(t), LUA_TTABLE); //传入GC链表 t->metatable = NULL; t->flags = cast_byte(~0); /* temporary values (kept only if some malloc fails) */ t->array = NULL; t->sizearray = 0; t->lsizenode = 0; t->node = cast(Node *, dummynode); setarrayvector(L, t, narray); setnodevector(L, t, nhash); return t; }
//将GCObject 串入GlobalState的rootgc中
void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { global_State *g = G(L); o->gch.next = g->rootgc; g->rootgc = o; o->gch.marked = luaC_white(g); o->gch.tt = tt; }
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n)
{ Closure *cl; lua_lock(L); luaC_checkGC(L); api_checknelems(L, n); cl = luaF_newCclosure(L, n, getcurrenv(L)); cl->c.f = fn; L->top -= n; while (n--) setobj2n(L, &cl->c.upvalue[n], L->top+n); setclvalue(L, L->top, cl); //将栈顶的Value指向Closure lua_assert(iswhite(obj2gco(cl))); api_incr_top(L); lua_unlock(L); }
//新建一个CClosure C闭包
Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e)
{ Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); luaC_link(L, obj2gco(c), LUA_TFUNCTION); //将c闭包串入gc root中 c->c.isC = 1; c->c.env = e; c->c.nupvalues = cast_byte(nelems); return c; }
新建一个lua 函数原型
Proto *luaF_newproto (lua_State *L)
{ Proto *f = luaM_new(L, Proto); luaC_link(L, obj2gco(f), LUA_TPROTO); 将lua Proto函数原型连入rootgc中 f->k = NULL; f->sizek = 0; f->p = NULL; f->sizep = 0; f->code = NULL; f->sizecode = 0; f->sizelineinfo = 0; f->sizeupvalues = 0; f->nups = 0; f->upvalues = NULL; f->numparams = 0; f->is_vararg = 0; f->maxstacksize = 0; f->lineinfo = NULL; f->sizelocvars = 0; f->locvars = NULL; f->linedefined = 0; f->lastlinedefined = 0; f->source = NULL; return f; }
新建一个Function 的 Upval
UpVal *luaF_newupval (lua_State *L)
{ UpVal *uv = luaM_new(L, UpVal); luaC_link(L, obj2gco(uv), LUA_TUPVAL); 将这个Upval链入到rootgc中 uv->v = &uv->u.value; setnilvalue(uv->v); return uv; }
新建一个LuaState 这是mainLuaState
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud)
{ int i; lua_State *L; global_State *g; void *l = (*f)(ud, NULL, 0, state_size(LG)); if (l == NULL)
return NULL; L = tostate(l); g = &((LG *)L)->g; L->next = NULL; L->tt = LUA_TTHREAD; g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); L->marked = luaC_white(g); 标记为白色 set2bits(L->marked, FIXEDBIT, SFIXEDBIT); preinit_state(L, g); g->frealloc = f; g->ud = ud; g->mainthread = L; g->uvhead.u.l.prev = &g->uvhead; g->uvhead.u.l.next = &g->uvhead; g->GCthreshold = 0; /* mark it as unfinished state */ g->strt.size = 0; g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(registry(L)); luaZ_initbuffer(L, &g->buff); g->panic = NULL; g->gcstate = GCSpause; g->rootgc = obj2gco(L); 由于是mainLuaState 所以就把rootgc直接指向LuaState g->sweepstrgc = 0; g->sweepgc = &g->rootgc; g->gray = NULL; g->grayagain = NULL; g->weak = NULL; g->tmudata = NULL; g->totalbytes = sizeof(LG); g->gcpause = LUAI_GCPAUSE; g->gcstepmul = LUAI_GCMUL; g->gcdept = 0; for (i=0; i<NUM_TAGS; i++)
g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0)
{ /* memory allocation error: free partial state */ close_state(L); L = NULL; } else luai_userstateopen(L); return L; }
创建一个额外的LuaState 则需要
LUA_API lua_State *lua_newthread (lua_State *L)
{ lua_State *L1; lua_lock(L); luaC_checkGC(L); L1 = luaE_newthread(L); setthvalue(L, L->top, L1); api_incr_top(L); lua_unlock(L); luai_userstatethread(L, L1); return L1; }
创建一个额外的LuaState
lua_State *luaE_newthread (lua_State *L)
{ lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); luaC_link(L, obj2gco(L1), LUA_TTHREAD); 将其连入rootgc中 preinit_state(L1, G(L)); stack_init(L1, L); /* init stack */ setobj2n(L, gt(L1), gt(L)); /* share table of globals */ L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); lua_assert(iswhite(obj2gco(L1))); return L
}
到现在 所有的GCObject中TString串入 GlobalState的全局字符串表 Udata连入luastate中的next 其余CClosure ,table,Lua proto,upvalue,luaState 都连入globalstate的rootgc中
值和变量的灭亡
luaGC 机制 和 过程
lua使用标记清除的算法 来进行垃圾回收 通过遍历可到达的对象并标记 之后不可达的对象就可以删除了 一般Lua的gc过程是不必直接调用的
LUA_API int lua_gc (lua_State *L, int what, int data) 可以用来控制gc
LUA_API int lua_gc (lua_State *L, int what, int data) { int res = 0; global_State *g; lua_lock(L); g = G(L); switch (what)
{ case LUA_GCSTOP:
{ g->GCthreshold = MAX_LUMEM; break; } case LUA_GCRESTART:
{ g->GCthreshold = g->totalbytes; break; } case LUA_GCCOLLECT:
{ luaC_fullgc(L); 这里会把gc全部做一遍 break; } case LUA_GCCOUNT:
{ /* GC values are expressed in Kbytes: #bytes/2^10 */ res = cast_int(g->totalbytes >> 10); break; } case LUA_GCCOUNTB:
{ res = cast_int(g->totalbytes & 0x3ff); break; } case LUA_GCSTEP:
{ //这里会把gc往前推进一步 lu_mem a = (cast(lu_mem, data) << 10); if (a <= g->totalbytes) g->GCthreshold = g->totalbytes - a; else g->GCthreshold = 0; while (g->GCthreshold <= g->totalbytes) { luaC_step(L); if (g->gcstate == GCSpause) { /* end of cycle? */ res = 1; /* signal it */ break; } } break; } case LUA_GCSETPAUSE: { res = g->gcpause; g->gcpause = data; break; } case LUA_GCSETSTEPMUL: { res = g->gcstepmul; g->gcstepmul = data; break; } default: res = -1; /* invalid option */ } lua_unlock(L); return res; }
隐式的gc过程都在这里 #define luaC_checkGC(L) { \ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ if (G(L)->totalbytes >= G(L)->GCthreshold) \ luaC_step(L); } 检查如果分配内存过大 GC会向前执行一步
GC的单步执行就在这里
void luaC_step (lua_State *L)
{ global_State *g = G(L); l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; if (lim == 0) lim = (MAX_LUMEM-1)/2; /* no limit */ g->gcdept += g->totalbytes - g->GCthreshold; do
{ lim -= singlestep(L); if (g->gcstate == GCSpause) gc过程做完了 break; } while (lim > 0); if (g->gcstate != GCSpause)
{ if (g->gcdept < GCSTEPSIZE) g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ else
{ g->gcdept -= GCSTEPSIZE; g->GCthreshold = g->totalbytes; } } else
{ lua_assert(g->totalbytes >= g->estimate); setthreshold(g); } }
gc真正的单步执行在这里
static l_mem singlestep (lua_State *L) { global_State *g = G(L); /*lua_checkmemory(L);*/ switch (g->gcstate)
{ case GCSpause: { markroot(L); /* start a new collection */ return 0; } case GCSpropagate: { if (g->gray) return propagatemark(g); else { /* no more `gray' objects */ atomic(L); /* finish mark phase */ return 0; } } case GCSsweepstring: { lu_mem old = g->totalbytes; sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ g->gcstate = GCSsweep; /* end sweep-string phase */ lua_assert(old >= g->totalbytes); g->estimate -= old - g->totalbytes; return GCSWEEPCOST; } case GCSsweep: { lu_mem old = g->totalbytes; g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) { /* nothing more to sweep? */ checkSizes(L); g->gcstate = GCSfinalize; /* end sweep phase */ } lua_assert(old >= g->totalbytes); g->estimate -= old - g->totalbytes; return GCSWEEPMAX*GCSWEEPCOST; } case GCSfinalize: { if (g->tmudata) { GCTM(L); if (g->estimate > GCFINALIZECOST) g->estimate -= GCFINALIZECOST; return GCFINALIZECOST; } else { g->gcstate = GCSpause; /* end collection */ g->gcdept = 0; return 0; } } default: lua_assert(0); return 0; } }
lua GC过程一共分为五步 Pause ,Propagate, SweepString,Sweep,finalize
1. Pause阶段 将主要部分进行标记
static void markroot (lua_State *L) { global_State *g = G(L); g->gray = NULL; globalState中 Gray List是所有的待处理对象 g->grayagain = NULL; g->weak = NULL; markobject(g, g->mainthread); /* make global table be traversed before main stack */ markvalue(g, gt(g->mainthread)); markvalue(g, registry(L)); markmt(g); g->gcstate = GCSpropagate; }
标记的东西有 globalstate中main LuaState, GlobalValue表, registry表, 以及markmt标记 basic type的metatables。 这之后GC进入一个搜索阶段 开始将仍然能够访问到的值进行标记
2. Propagate阶段 将所有仍然存活的值进行标记 这个过程是渐进的
case GCSpropagate: { if (g->gray) //Propagate的终结在于gray待处理链表已经为空 所有的存活对象已经全部扫描到了 return propagatemark(g); else
{ /* no more `gray' objects */ atomic(L); /* finish mark phase */ return 0; }
在递归渐进的过程中 标记所有可以访问到的值 初次进入这里的markroot中 一共标记luaState 和 Table两种形式
static l_mem propagatemark (global_State *g) { GCObject *o = g->gray; lua_assert(isgray(o)); gray2black(o); //在渐进过程中 所有可以访问到的gray物体 都会被变成black的 switch (o->gch.tt)
{ case LUA_TTABLE: { Table *h = gco2h(o); g->gray = h->gclist; if (traversetable(g, h)) /* table is weak? */ 如果table中有weak标记 重新将table变为gray black2gray(o); /* keep it gray */ 出于什么考虑 这里并没有把table重新串回global的gray 链表 ??? return sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(Node) * sizenode(h); } case LUA_TFUNCTION: { Closure *cl = gco2cl(o); g->gray = cl->c.gclist; //为什么只有CCLosure才有 只对C 闭包做这个 traverseclosure(g, cl); return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : sizeLclosure(cl->l.nupvalues); } case LUA_TTHREAD: { lua_State *th = gco2th(o); g->gray = th->gclist; th->gclist = g->grayagain; g->grayagain = o; black2gray(o); traversestack(g, th); return sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * th->size_ci; } case LUA_TPROTO: { Proto *p = gco2p(o); g->gray = p->gclist; traverseproto(g, p); return sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto *) * p->sizep + sizeof(TValue) * p->sizek + sizeof(int) * p->sizelineinfo + sizeof(LocVar) * p->sizelocvars + sizeof(TString *) * p->sizeupvalues; } default: lua_assert(0); return 0; } }
//初始table无metatable 不标记weakkey 不标记weakvalue 全部都是强引用 即只要table是活的 内部的key 与 value就都是活的了
遍历table 同时将能够访问的值都标记 static int traversetable (global_State *g, Table *h) { int i; int weakkey = 0; int weakvalue = 0; const TValue *mode; if (h->metatable) markobject(g, h->metatable); 标记Table的metatable mode = gfasttm(g, h->metatable, TM_MODE); if (mode && ttisstring(mode)) { /* is there a weak mode? */ weakkey = (strchr(svalue(mode), 'k') != NULL); 获取table的weakkey 与 weakvalue标记标志 weakvalue = (strchr(svalue(mode), 'v') != NULL); if (weakkey || weakvalue) { /* is really weak? */ h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ h->marked |= cast_byte((weakkey << KEYWEAKBIT) | (weakvalue << VALUEWEAKBIT)); h->gclist = g->weak; /* must be cleared after GC, ... */ 把这个table链入gc 的 weak 链表中 出于什么考虑 g->weak = obj2gco(h); /* ... so put in the appropriate list */ } } if (weakkey && weakvalue) return 1; //weakkey 和 weakvalue都存在 这是一个彻底的weak table 他不能决定table自己内部的key 与 value的生死 所以不用继续向下标记了 if (!weakvalue) { //如果没标记weak value 那么数组的部分的存活状况是由table来决定的 所以数组部分统统标记 i = h->sizearray; while (i--) markvalue(g, &h->array[i]); } i = sizenode(h); while (i--) { Node *n = gnode(h, i); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); if (ttisnil(gval(n))) //table内的 key value对的value已经被置nil 了 可以从table中移除这个key & value pair了 removeentry(n); /* remove empty entries */ else { lua_assert(!ttisnil(gkey(n))); if (!weakkey) markvalue(g, gkey(n)); // 没有标记weak key 则key的存活决定于table if (!weakvalue) markvalue(g, gval(n)); // 没有标记weak value 则value的存活决定于table } } return weakkey || weakvalue; }
遍历闭包 将可访问的值进行标记
static void traverseclosure (global_State *g, Closure *cl) { markobject(g, cl->c.env); 标记闭包的环境表 if (cl->c.isC) { //标记UpVals 只要Closure可以访问到 那么那些Upvals 也应该可以访问到 int i; for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->c.upvalue[i]); } else { int i; lua_assert(cl->l.nupvalues == cl->l.p->nups); markobject(g, cl->l.p); for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */ markobject(g, cl->l.upvals[i]); } }
遍历LuaState 标记堆栈中的活动值 对CallInfo的调用层次 要继续看
static void traversestack (global_State *g, lua_State *l) { StkId o, lim; CallInfo *ci; markvalue(g, gt(l)); //标注GlobalValues lim = l->top; //找到当前活动堆栈的顶端 通过调用层次来 从最基本CallInfo 到 当前的CallInfo for (ci = l->base_ci; ci <= l->ci; ci++)
{ lua_assert(ci->top <= l->stack_last); if (lim < ci->top) lim = ci->top; } // StkId stack; /* stack base */ 从堆栈底开始 // StkId top; /* first free slot in the stack */ 到第一个可用堆栈 for (o = l->stack; o < l->top; o++) markvalue(g, o); //这些堆栈数据时当前的LuaState都可以访问的数据 for (; o <= lim; o++) setnilvalue(o); //堆栈中达不到的数据 主动释放 这一步可省略?? checkstacksizes(l, lim); }
标记Lua 函数声明
static void traverseproto (global_State *g, Proto *f) { int i; if (f->source) stringmark(f->source); 标记lua的src字符串 for (i=0; i<f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); 标记Lua Functino中用到的const 常量表 for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */ if (f->upvalues[i]) 标记Lua Function中用到的Upvalue Names 注意是名字 stringmark(f->upvalues[i]); } for (i=0; i<f->sizep; i++) { /* mark nested protos */ if (f->p[i]) markobject(g, f->p[i]); 标记内嵌在lua Function中的 lua Function定义 } for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */ if (f->locvars[i].varname) stringmark(f->locvars[i].varname); 标记定义在lua Function内部的局部变量的Names 注意又是名字 } }
Proto只是一个函数原型 所以他标记的那些.. 运行机制 Proto运行以后 产生CallInfo以后 才会在LuaState 的 stack中产生变量与值
在真正mark的时候 牵扯到一个递归的过程
static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o); switch (o->gch.tt) { case LUA_TSTRING: {
字符串mark不需要做额外的操作 return; } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; gray2black(o); /* udata are never gray */ if (mt) markobject(g, mt); 如果metatable存在 mark它 这又是一个递归的过程 markobject(g, gco2u(o)->env); mark userdata的env环境表 return; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v == &uv->u.value) /* closed? */ gray2black(o); /* open upvalues are never black */ return; } case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; 如果mark到 function 那么需要propagate g->gray = o; break; } case LUA_TTABLE: { gco2h(o)->gclist = g->gray; table需要propagate g->gray = o; break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; LuaState 需要propagate g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; Proto需要propagate g->gray = o; break; } default: lua_assert(0); } }
static void atomic (lua_State *L) { global_State *g = G(L); size_t udsize; /* total size of userdata to be finalized */ /* remark occasional upvalues of (maybe) dead threads */ remarkupvals(g); /* traverse objects cautch by write barrier and by 'remarkupvals' */ propagateall(g); /* remark weak tables */ g->gray = g->weak; g->weak = NULL; lua_assert(!iswhite(obj2gco(g->mainthread))); markobject(g, L); /* mark running thread */ markmt(g); /* mark basic metatables (again) */ propagateall(g); /* remark gray again */ g->gray = g->grayagain; g->grayagain = NULL; propagateall(g); udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ marktmu(g); /* mark `preserved' userdata */ udsize += propagateall(g); /* remark, to propagate `preserveness' */ cleartable(g->weak); /* remove collected objects from weak tables */ /* flip current white */ //变换当前的white标志位 这样新创建出来的对象都不会被计入这次的GC过程 g->currentwhite = cast_byte(otherwhite(g)); g->sweepstrgc = 0; g->sweepgc = &g->rootgc; sweepgc 指向 rootgc 开始 g->gcstate = GCSsweepstring; g->estimate = g->totalbytes - udsize; /* first estimate */ }
在整个propagate过程完成后 进行当前白色位变换 这样新创建出来的对象都不会被计入这次的GC过程 之后sweepgc指向rootgc 然后进入SweepString过程 开始垃圾清理
atomic中 有一个重要的步骤是 luaC_separateudata 将已经死亡且需要调用 GC method函数的Userdata分出来 放入globalState GCObject *tmudata; /* last element of list of userdata to be GC */ 在gc的最后在执行对这部分Userdata的垃圾回收 没有GC函数的Userdata会被直接回收内存
清理字符串阶段 在这一阶段所有的字符串会被清理
case GCSsweepstring: { lu_mem old = g->totalbytes; sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); g->sweepstrgc在当前的值为0 if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ 全部扫描完毕了 进入扫除其他垃圾的阶段 g->gcstate = GCSsweep; /* end sweep-string phase */ lua_assert(old >= g->totalbytes); g->estimate -= old - g->totalbytes; return GCSWEEPCOST; }
static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { GCObject *curr; global_State *g = G(L); int deadmask = otherwhite(g); 旧的白色位为死亡标志位 while ((curr = *p) != NULL && count-- > 0) { if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ sweepwholelist(L, &gco2th(curr)->openupval); if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); makewhite(g, curr); /* make it white (for next cycle) */ //将未死的对象重新标记为current white p = &curr->gch.next; } else { /* must erase `curr' */ lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); *p = curr->gch.next; if (curr == g->rootgc) /* is the first element of the list? */ g->rootgc = curr->gch.next; /* adjust first */ freeobj(L, curr); //内存释放步骤 } } return p; }
其他类型的垃圾清理阶段
case GCSsweep: { lu_mem old = g->totalbytes; g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) { /* nothing more to sweep? */ checkSizes(L); g->gcstate = GCSfinalize; /* end sweep phase */ } lua_assert(old >= g->totalbytes); g->estimate -= old - g->totalbytes; return GCSWEEPMAX*GCSWEEPCOST; }
以及最后的finalize阶段
case GCSfinalize: { if (g->tmudata) 如果有要死当时要执行 gc method的Userdata 执行它的gc method { //Userdata 在这一步内存还未被释放吗 ??? GCTM(L); if (g->estimate > GCFINALIZECOST) g->estimate -= GCFINALIZECOST; return GCFINALIZECOST; } else 没有 这次的gc过程结束了 { g->gcstate = GCSpause; /* end collection */ g->gcdept = 0; return 0; } }
//执行Userdata的GC method
static void GCTM (lua_State *L) { global_State *g = G(L); GCObject *o = g->tmudata->gch.next; /* get first element */ Udata *udata = rawgco2u(o); const TValue *tm; /* remove udata from `tmudata' */ if (o == g->tmudata) /* last element? */ g->tmudata = NULL; else g->tmudata->gch.next = udata->uv.next; udata->uv.next = g->mainthread->next; /* return it to `root' list */ g->mainthread->next = o; makewhite(g, o); tm = fasttm(L, udata->uv.metatable, TM_GC); if (tm != NULL) { lu_byte oldah = L->allowhook; lu_mem oldt = g->GCthreshold; L->allowhook = 0; /* stop debug hooks during GC tag method */ g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ setobj2s(L, L->top, tm); setuvalue(L, L->top+1, udata); L->top += 2; luaD_call(L, L->top - 2, 0); L->allowhook = oldah; /* restore hooks */ g->GCthreshold = oldt; /* restore threshold */ } }
GCTM将Userdata的GC method拿来执行以下 同时为这个Userdata标记CurrentWhite 因为在前面标记带GC Method的死亡Userdata时 已经为Userdata标记了finalize标记 所以在下次的GC过程中 它将不
在执行GC method 而直接进行内存释放 所以一个带GC method的Userdata 它的完全回收过程 会经历两个GC过程
/* move `dead' udata that need finalization to list `tmudata' */ size_t luaC_separateudata (lua_State *L, int all) {
......
if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) //一个已经执行难过GC Method的Userdata会被直接进行垃圾回收 不会出现GC method重复执行的问题 p = &curr->gch.next; /* don't bother with them */
....
}
这样Lua 的 GC 垃圾回收就完了
|
请发表评论