1. 程式人生 > >lua原始碼分析-gc篇(二)資料結構

lua原始碼分析-gc篇(二)資料結構

1. 棧
定義
簡單的圖
2.棧元素TValue
定義
說明
3.GC物件
定義
說明
4.gc連結串列
定義
5.棧和gc連結串列的關係
答疑解惑
總結

這是這個gc系列的第二篇,這一篇主要講GC用到的資料結構,有助於理解gc的,所以放在前面

1. 棧

棧就是我們平時寫程式碼接到的lua_State。在實現上是用一個數組實現的。每個成員型別是TValue,看下文詳細介紹。這裡不打算詳細介紹棧的結構和內容,只會介紹和gc相關的一些內容,主要是幫助我們更好的理解lua的gc。

定義

下面是lua_State的定義,會看到棧的身影,top和base指標。

 
struct lua_State {
  CommonHeader;
  lu_byte status;
  StkId
top;  /* first free slot in the stack */
  StkId base;  /* base of current function */
  global_State *l_G;
  CallInfo *ci;  /* call info for current function */
  const
Instruction *savedpc;  /* `savedpc' of current function */
  StkId stack_last;  /* last free slot in the stack */
  StkId stack;  /* stack base */
  CallInfo *end_ci;  /* points after end of ci array*/
  CallInfo *base_ci;  /* array of CallInfo's */
  int stacksize;
  int size_ci;  /* size of array `base_ci' */
  unsigned short nCcalls;  /* number of nested C calls */
  lu_byte hookmask;
  lu_byte allowhook;
  int basehookcount;
  int hookcount;
  lua_Hook hook;
  TValue l_gt;  /* table of globals */
  TValue env;  /* temporary place for environments */
  GCObject *openupval;  /* list of open upvalues in this stack */
  GCObject *gclist;
  struct lua_longjmp *errorJmp;  /* current error recover point */
  ptrdiff_t errfunc;  /* current error handling function (stack index) */
};

簡單的圖



至於base/top和stack/stack_last,以及base_ci/end_ci之間的關係和區別就不打算詳細介紹了。主要是lua的指令操作的實現也是在棧上實現的,以及函式呼叫的時候也要用到棧,只是他們在這個陣列的不同區間。

2.棧元素TValue

這個型別是給棧用的,前面說過棧其實是一個TValue的陣列。

定義

 
typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;
#define TValuefields    Value value; int tt
typedef struct lua_TValue {
  TValuefields;
} TValue;

說明

  • TValue,是Value加了一個型別
    型別定義:
 
/*
** basic types
*/
#define LUA_TNONE        (-1)
#define LUA_TNIL        0
#define LUA_TBOOLEAN        1
#define LUA_TLIGHTUSERDATA    2
#define LUA_TNUMBER        3
#define LUA_TSTRING        4
#define LUA_TTABLE        5
#define LUA_TFUNCTION        6
#define LUA_TUSERDATA        7
#define LUA_TTHREAD        8
  • 可以看到存放真正值的是這是一個union結構
  • 用過lua的都知道,lua是一種動態型別語言,所有值都是first-class的。所以程式碼層就是這個Value
  • 簡單介紹一下union中成員的含義
成員 含義
GCObject *gc 所有的需要gc的物件都是用的這個成員,所以本系列文章只關注這個成員就好了
void *p 存放lightuserdata
lua_Number n 數值型別,這裡也可以看出來lua裡面用到的整形浮點型都是用這個儲存的,就是double型別
int b bool型別

看程式碼:

 
// bool型別的巨集
#define setbvalue(obj,x) \
  { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
// table型別的巨集
#define sethvalue(L,obj,x) \
  { TValue *i_o=(obj); \
    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
    checkliveness(G(L),i_o); }

3.GC物件

gc物件就是指lua裡面需要被回收的物件,型別是在LUA_TSTRING(4)到LUA_TTHREAD(8)之間(準確來說還有擴充套件的型別)。開始看的時候,難免會有疑問,lua裡面的所有物件不都是放在棧裡面的嗎?這個gc物件是個什麼的存在?

定義

 
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 */
};
// head
#define CommonHeader    GCObject *next; lu_byte tt; lu_byte marked

說明

  • 這是一個union結構體
  • 這裡必須提醒注意下這個GCheader,可以看到前面一個GCheader gch的定義,是跟型別無關的。後面在解答上面疑問的時候,一併說明一下。這個CommonHeader實現了一個連結串列結構(next),也指明瞭這個物件的型別(tt),以及顏色(marked)。
  • 會看到除了前面的基本型別之外,多了幾個可以回收的型別
 
/*
** Extra tags for non-values
*/
#define LUA_TPROTO    (LAST_TAG+1)
#define LUA_TUPVAL    (LAST_TAG+2)
#define LUA_TDEADKEY    (LAST_TAG+3)

4.gc連結串列

這個連結串列是記錄了lua裡面所有的可回收物件,另外注意這是一個單向連結串列。真因為是單向連結串列,為了效率,才不會去整個遍歷一遍,才會再增加掃描的連結串列等,這些後面詳細介紹。

定義

這個連結串列的指標是放在global_State中rootgc中的。

 
/*
** `global state', shared by all threads of this state
*/
typedef struct global_State {
  stringtable strt;  /* hash table for strings */
  lua_Alloc frealloc;  /* function to reallocate memory */
  void *ud;         /* auxiliary data to `frealloc' */
  lu_byte currentwhite;
  lu_byte gcstate;  /* state of garbage collector */
  int sweepstrgc;  /* position of sweep in `strt' */
  GCObject *rootgc;  /* list of all collectable objects */
  GCObject **sweepgc;  /* position of sweep in `rootgc' */
  GCObject *gray;  /* list of gray objects */
  GCObject *grayagain;  /* list of objects to be traversed atomically */
  GCObject *weak;  /* list of weak tables (to be cleared) */
  GCObject *tmudata;  /* last element of list of userdata to be GC */
  Mbuffer buff;  /* temporary buffer for string concatentation */
  lu_mem GCthreshold;
  lu_mem totalbytes;  /* number of bytes currently allocated */
  lu_mem estimate;  /* an estimate of number of bytes actually in use */
  lu_mem gcdept;  /* how much GC is `behind schedule' */
  int gcpause;  /* size of pause between successive GCs */
  int gcstepmul;  /* GC `granularity' */
  lua_CFunction panic;  /* to be called in unprotected errors */
  TValue l_registry;
  struct lua_State *mainthread;
  UpVal uvhead;  /* head of double-linked list of all open upvalues */
  struct Table *mt[NUM_TAGS];  /* metatables for basic types */
  TString *tmname[TM_N];  /* array with tag-method names */
} global_State;

這個結構放了所有關於gc的內容(對著後面的註釋看一下):

  • currentwhite:這個就是第一篇中提到的gc流程中的當前白色,如果清理階段某個物件是otherwhite,那麼他就會被清理掉
  • gcstate:控制gc流程的,後面流程中說的狀態就是記錄在這裡
  • rootgc:前面剛提到過,所有可回收的gc物件單向連結串列
  • gray:為了gc的效率增加的一個gc連結串列
  • grayagain:為了實現增量式gc,過程中處理中斷問題的一個連結串列
  • GCthreshold,totalbytes,estimate,gcdept,gcpause:這幾個單次gc相關的控制或者狀態量,直接關係到lua提供的介面collectgarbage
  • 另外一些是全域性的一些變數的定義,metatable等。這些跟gc掃描不會遍歷整個gc連結串列有關係。

5.棧和gc連結串列的關係

棧沒有細說,但是他和gc連結串列的關係必須詳細說明一下。如下圖所示


答疑解惑

就著問題,說一下棧和gc連結串列之間的關係。

  • 1.GCObject的存在
    這裡就需要了解gc連結串列和棧中元素的關係。lua的棧是一個數組,裡面真正存放了lua裡面的所有物件。gc連結串列存放了lua所有的可回收物件,而事實上gc連結串列存放的只是所有可回收物件的指標,真正的物件還是以TValue(GCObject* gc成員)的形式放在lua棧中的。當然,物件真正的內容是在堆上(需要自己回收)。而棧和gc連結串列中存在的只是真實物件的指標,不同型別的結構不一樣,所以以這種方式才能存在一起管理

  • 2.怎麼做到的?
    前面提到了CommonHead,它在棧和gc連結串列關係中起了關鍵的作用。有相同的頭部,所以可以通過強制轉換在TValue和GCObject直接為了當時需要進行切換.看原始碼更清晰了,能夠轉換為GCObject的結構體都是必須包含這個頭部的,需要GC的結構都要新增這個頭部,如下所所示:

 
//
struct lua_State {
  CommonHeader;
//
typedef struct Table {
  CommonHeader;
// 
typedef struct UpVal {
  CommonHeader;
//
typedef struct Proto {
  CommonHeader;
//
typedef union Udata {
  L_Umaxalign dummy;  /* ensures maximum alignment for `local' udata */
  struct {
    CommonHeader;
typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;

總結

  • 這裡並沒有把所有的結構體都解釋一遍,userdata,upvalue相關的都是比較細節的東西,可以單獨看相關的內容,在全域性理解的情況下根據自身特性去看也會很容易明白