從Lua5.1.4源碼來分析Lua的GC機制(一)

注意:5.1之后就開始使用增量式的收集器,也就是說它是隔行掃描的方式與解釋器一起工作。而5.1之前,收集器的運行會暫停對整個程序的響應。這一個改變還是非常有用處的!

1. gc的狀態(tài)階段

gc被分為下面五個階段:標記、整理、清掃字符串、清掃、收尾
狀態(tài)值的大小代表了它們的執(zhí)行順序,越小越先執(zhí)行。

#define GCSpause    0
#define GCSpropagate    1
#define GCSsweepstring  2
#define GCSsweep    3
#define GCSfinalize 4

2. GCSpause

GCSpause只是用來標記系統(tǒng)的根節(jié)點


GCSpause的執(zhí)行

Lua認為每個需要被GC管理的對象都有顏色,一開始,所有節(jié)點都是白色的。在我們標記階段,將節(jié)點逐個被設置為黑色。有一些節(jié)點因為還存在子節(jié)點還沒有處理,所以為灰色。在下面的源碼中我們可以看出,根節(jié)點含有其他子節(jié)點,所以根節(jié)點我們先將它設置為灰色。

markroot的實現(xiàn)方式

在上面的源碼中我們還發(fā)現(xiàn)了markobject這個方法,點進去看一下它到底是如何標記的,它調用了另外一個函數(shù)reallymarkobject:

static void reallymarkobject (global_State *g, GCObject *o) {
  lua_assert(iswhite(o) && !isdead(g, o));
  white2gray(o);
  switch (o->gch.tt) {
    case LUA_TSTRING: {
      return;
    }
    case LUA_TUSERDATA: {
      Table *mt = gco2u(o)->metatable;
      gray2black(o);  /* udata are never gray */
      if (mt) markobject(g, mt);
      markobject(g, gco2u(o)->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;
      g->gray = o;
      break;
    }
    case LUA_TTABLE: {
      gco2h(o)->gclist = g->gray;
      g->gray = o;
      break;
    }
    case LUA_TTHREAD: {
      gco2th(o)->gclist = g->gray;
      g->gray = o;
      break;
    }
    case LUA_TPROTO: {
      gco2p(o)->gclist = g->gray;
      g->gray = o;
      break;
    }
    default: lua_assert(0);
  }
}

可以看出,字符串對象并沒有任何的操作直接返回

  • userdata是將它本身及它的元表和環(huán)境表標為黑色(這是一個遞歸的過程,不斷去找尋它的元表)
  • upValue也就是自由變量,當它脫離了它的作用域之后就會被設置為黑色,這里大家可能會對 if (uv->v == &uv->u.value) 產生疑惑,沒關系,我們再深入研究一下UpValue的概念以及它的新建與關閉。
  • 其他都是函數(shù)、表、線程。它們就是通過對象保存的上一階段的全局灰鏈,然后將自身賦予灰鏈,并且標記自己為黑色,這樣就能把強引用的對象連接起來,在擴散標記階段,處理標記灰鏈中對象的時候,處理完子引用后,就可以通過對象的gclist指針恢復上一個灰鏈。 (?不是很理解)
    Lua引用的實現(xiàn)
    通過為每個變量至少創(chuàng)建一個upvalue 并按所需情況進行重復利用,保證了未脫離作用域的局部變量可以在閉包里正確使用。為了保證它的唯一性,Lua為整個運行棧保存了一個鏈接著所有正打開(正指向棧內的局部變量)的upValue的鏈表。所以當Lua在創(chuàng)建一個新的閉包的時候,就會遍歷外面的所有局部變量,對于這些變量,如果在鏈表里有找到它,就重用。否則創(chuàng)建一個新的upValue并保存到鏈表中。
    再了解一下upValue的結構(看注釋即可明白)
typedef struct UpVal {  
  CommonHeader;  
  TValue *v;  /* points to stack or to its own value */  
  union {  
    TValue value;  /* the value (when closed) */  
    struct {  /* double linked list (when open) */  
      struct UpVal *prev;  
      struct UpVal *next;  
    } l;  
  } u;  
} UpVal;

如下我們可以看到,不斷的遍歷外部的局部變量,如果不存在upValue的就新建它,那么此時它就是不關閉的

UpVal *luaF_findupval (lua_State *L, StkId level) {  
  global_State *g = G(L);  
///得到openupval鏈表  
  GCObject **pp = &L->openupval;  
  UpVal *p;  
  UpVal *uv;  
///開始遍歷open upvalue。  
  while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {  
    lua_assert(p->v != &p->u.value);  
///發(fā)現(xiàn)已存在。  
    if (p->v == level) {    
      if (isdead(g, obj2gco(p)))  /* is it dead? */  
        changewhite(obj2gco(p));  /* ressurect it */  
///直接返回  
      return p;  
    }  
    pp = &p->next;  
  }  
///否則new一個新的upvalue  
  uv = luaM_new(L, UpVal);  /* not found: create a new one */  
  uv->tt = LUA_TUPVAL;  
  uv->marked = luaC_white(g);  
///設置值  
  uv->v = level;  /* current value lives in the stack */  
///首先插入到lua_state的openupval域  
  uv->next = *pp;  /* chain it in the proper position */  
  *pp = obj2gco(uv);  
///然后插入到global_State的uvhead(這個也就是雙向鏈表的頭)  
  uv->u.l.prev = &g->uvhead;  /* double link it in `uvhead' list */  
  uv->u.l.next = g->uvhead.u.l.next;  
  uv->u.l.next->u.l.prev = uv;  
  g->uvhead.u.l.next = uv;  
  lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);  
  return uv;  
}  

所以我們才在下面的if語句里判斷upValue是否為關閉,如果為關閉就將它標記為黑色。未關閉表示現(xiàn)在局部變量還沒有出作用域!所以不能標記為黑!


upValue

這里有兩個白色標記位。在GC標記結束但是清理流程尚未作完前,一旦對象間的關系產生了變化,比如有新增對象的時候,我們不知道它的生命期,那么我們就不能夠將它簡單的設置為黑色,所以這時候就引出了一個第二種白色的概念


節(jié)點的顏色存儲

GCSpause 階段執(zhí)行完,立刻就將狀態(tài)切換到了 GCSpropagate ,我們下篇再來深入講解GCSpropagate

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容