• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

关于Lua一点分析

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

一.变量和值

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


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
qt添加lua支持发布时间:2022-07-22
下一篇:
LUAOOP编程实现方法发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap