关于Lua一点分析

一.变量和值

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 垃圾回收就完了