Lua源碼分析-GC原理

GC的原理

  首先Lua是以union+type的形式來保存值的。如果是需要被GC管理的值,就以GCObject指針的形式保存,否則就直接存值。而所有的GCObject都有相同的CommonHeader,在CommonHeader中next域用來串聯單鏈表,tt用來識別類型,marked用來標記清除工作。源碼如下:
  

lobject.h

#define CommonHeader    GCObject *next; lu_byte tt; lu_byte marked

typedef struct GCheader {
  CommonHeader;
} GCheader;

typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;

  實現時,並不是通過一條鏈表維護的。對於string來說,所有的string放在一張大的hash表中,以保證系統中不會有值相同的string被創建兩份。所以string是單獨管理的,不再GCObject的鏈表中。
  還有其他特殊類型,如thread、userdata都會在GC時進行特殊處理。

   GC分爲5個階段,在lgc.h中的singlestep方法中實現。GCSpause階段完成後會立刻切換GCSpropagate,GCSpropagate階段會分步完成,當檢測到有未標記的對象時,迭代進行標記。之後進GCSsweepstring階段,GCSsweepstring階段主要用於清理Lua中的string,這個階段中每一步會清理hash表的一列。之後是GCSsweep階段,和GCSsweepstring階段類似。最後是GCSfinalize階段,會在這個階段對有GC元方法的userdata對象調用它GC方法,並把userdata作爲參數傳入。源碼如下:
  


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管理對象的都會有一個顏色,所有對象一創建出來都是白色,是可以清理的,在標記階段會將簡單的對象設置爲不可清理的黑色,對於複雜的會關聯到其他對象的對象,在沒處理完其他對象時,會標記爲灰色,當清理階段結束之後會將所有對象設置爲白色,在下次GC時再重新設置。這裏面白色會有兩種:0和1。有兩個白色是因爲,Lua的GC是單遍掃描的,所以當標記流程結束,但是清理流程沒有結束時,當有新的對象產生時,由於無法預測如果直接標記爲黑色,可能會導致無法清除。而兩種白色就是一個開關,當前清理0白色時,則保護1白色,反之也是一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章