Schemeをつくろう(21) フリーリスト作り

GCの本格運用のためには、フリーリストが不可欠。ということでフリーリスト作り。


その前に復習。

データ構造

オブジェクトの表現は、2個の構造体で表現することにした。

+--------------+    +--------+
| オブジェクト | -> | データ |
+--------------+    +--------+
    V
  オブジェクト
       V

オブジェクト全体は線形リストで繋がってます。

GCの実装

マーク・アンド・スイープ方式を取る。

  • 線形リストでオブジェクトを繋ぐ。
  • レジスタ内を走査してマークする。
  • 繋いだ線形リストからマークの無い未使用のオブジェクトを開放する。

前回はここまで。


では、いつGCをするのか考える。

GCのタイミング

コードリーディングから、オブジェクト確保時GCするのがセオリーっぽい事がわかったので、オブジェクト確保時にGCすることにします。

しかし、今のまま実装してしまうと毎回GCしてしまう事になるので、フリーリストを導入し、フリーリストが空になったらGCして、それでもフリーリストが空ならばmallocすることに。

というわけで、フリーリスト作り

オブジェクト開放時に、オブジェクトとデータという構造体を2個管理しなければならないのは大変なので、

完全に分離しないで、(オブジェクト+データ)という単位ごとにフリーリストを作っておくことにした。

(オブジェクト+データ)という単位で扱えば、フリーリストは配列で作れる。

object gc_free_objects[TYPE_LENGTH];

かなりらくちん管理だ。

フリーリストの枯渇

作っていくと問題がいろいろ。

フリーリストの枯渇状態が続くと、

> (+ 1 2 3 4 5 6 7 8 9 10)
gc...
gc...
gc...
gc...
...

いっぱいGCしてしまう。


問題は、フリーリストが無くなったら、オブジェクトを1個しか確保しないことにある。

1個使ってしまうので、またGCが必要になる。という連鎖が起こってしまう。


そこで、メモリの確保要求が来たら、少し余分にフリーリストに貯めておくことにする。


改善後は、リストの枯渇、シンボルの枯渇、数値の枯渇で、GCは3回に抑えられた。


レジスタに繋がっていない状態のオブジェクト

もうひとつの問題。

レジスタに繋がっていないオブジェクトが存在した状態でGCが走ると、繋がっていないオブジェクトは全て破棄されてしまう。

例えば、consするとき

value = cons(1, cons(2, cons(3, value)));

(valueSchemeレジスタ、数は数オブジェクトとする)

と、チェーンでconsしてしまうと。

value = cons(1, cons(2, cons(3, value)));
             // ↑GCが走ると ↑破棄される

途中のconsはレジスタに繋がっていないので、破棄されてしまうオブジェクトが発生する。かなり大問題。


GCを正しく行うには、

args = 3;
value = cons(args, value);
args = 2;
value = cons(args, value); // GCしてもレジスタに繋がってるから安心
args = 1;
value = cons(args, value);

と、Schemeレジスタに必ず繋がった状態を保ちながらconsする必要がある。


書き換えが面倒なので、GC禁止フラグを立てて対処してみた。

次の課題

  • レジスタに繋がっていないオブジェクトが存在する」問題について考える。
  • フリーリストへの確保量が多ければGC回数は劇的に下がるが、メモリの無駄は劇的に増える。バランスをどうするか。
  • 終了時以外にOSにメモリを返すことが無いのでOSにメモリを返すタイミングも考える。

GCなんて簡単だ!と思ってたけど、かなり罠が多い。

「独自GCなんか捨ててしまって、Boehm GC使ったほうがいい」

とも思うけど、勉強のためにもう少し続けてみたい。