GCのサンプルとしてなんかのソースを見てみようということで、Lua を見てみる。 5.1 からはインクリメンタルGCだということなのでもっとシンプルな、4.0の古きよきマーク&スイープ版を見てみる。
GCの関するヘッダの lgc.h に定義されてるのは2つの関数のみ:
void luaC_collect (lua_State *L, int all); |
男らしい。
GCのメインルーチン luaC_collectgarbage()
でやってることは:
- 全てマーク
- 参照を無効化
- 回収
- Mバッファチェック(?)
- しきい値を現在のブロック確保数の2倍に
gcTM
呼び出し(?)
static void luaC_collectgarbage (lua_State *L) { |
細かいことは置いといて、中でやってるのはまさにマーク&スイープ。 Lua で楽ちんなところは、C との値のやり取りをする API がすべてLua側が管理してるスタックのインデクスを使って行われるので、C の変数やレジスタが参照してるけど Lua 側から辿れないのでマークされずに回収されてしまう、ということが起きなくて漏れなく簡単にマークできる。
でマークする Lua オブジェクトの種類によって、内部で使ってるオブジェクトをさらにマークしていく。 Lua のオブジェクトでメモリ確保が必要なものは
- テーブル
- クロージャ
のみ、というのも簡単でよい。
4.0 では Lua のメモリ管理はヒープ領域を自分で管理してるわけではなく単に標準関数の realloc()
を呼び出している:
で luaM_new
で確保したメモリは種類によって lua_State
構造体の中に保持して、GC時にたどってマークがされてるかどうか確認できるようにしてある。
しかしこのように作るのって結構しんどいと感じる。 もっと楽にできないものか…。
- これが正確GCか
- ポインタがヒープ内のブロックの先頭を指してるか、は確認しない
- 既存のGC手法の長所と課題 - Matzにっき リファレンスカウンタは負荷が分散されるしいいかもなぁ