Lua-4.0のGC

2008-10-29

GCのサンプルとしてなんかのソースを見てみようということで、Lua を見てみる。 5.1 からはインクリメンタルGCだということなのでもっとシンプルな、4.0の古きよきマーク&スイープ版を見てみる。

GCの関するヘッダの lgc.h に定義されてるのは2つの関数のみ:

void luaC_collect (lua_State *L, int all);
void luaC_checkGC (lua_State *L);

男らしい。 GCのメインルーチン luaC_collectgarbage() でやってることは:

  • 全てマーク
  • 参照を無効化
  • 回収
  • Mバッファチェック(?)
  • しきい値を現在のブロック確保数の2倍に
  • gcTM 呼び出し(?)
static void luaC_collectgarbage (lua_State *L) {
markall(L);
invalidaterefs(L); /* check unlocked references */
luaC_collect(L, 0);
checkMbuffer(L);
L->GCthreshold = 2*L->nblocks; /* set new threshold */
callgcTM(L, &luaO_nilobject);
}

細かいことは置いといて、中でやってるのはまさにマーク&スイープ。 Lua で楽ちんなところは、C との値のやり取りをする API がすべてLua側が管理してるスタックのインデクスを使って行われるので、C の変数やレジスタが参照してるけど Lua 側から辿れないのでマークされずに回収されてしまう、ということが起きなくて漏れなく簡単にマークできる。

でマークする Lua オブジェクトの種類によって、内部で使ってるオブジェクトをさらにマークしていく。 Lua のオブジェクトでメモリ確保が必要なものは

  • テーブル
  • クロージャ

のみ、というのも簡単でよい。

4.0 では Lua のメモリ管理はヒープ領域を自分で管理してるわけではなく単に標準関数の realloc() を呼び出している:

#define luaM_free(L, b)		luaM_realloc(L, (b), 0)
#define luaM_malloc(L, t) luaM_realloc(L, NULL, (t))
#define luaM_new(L, t) ((t *)luaM_malloc(L, sizeof(t)))
#define luaM_newvector(L, n,t) ((t *)luaM_malloc(L, (n)*(lint32)sizeof(t)))

luaM_new で確保したメモリは種類によって lua_State 構造体の中に保持して、GC時にたどってマークがされてるかどうか確認できるようにしてある。

しかしこのように作るのって結構しんどいと感じる。 もっと楽にできないものか…。