この問題を解決する方式はいくつか考えられるが、KLICでは以下に述べる ボトムアップコピー方式と暗黙前方参照方式を実装した。 (それらは条件コンパイルにより切り替えるようにしている)
しかしこの方式で共有メモリ変数は例外にしなければならない。GCノードは、旧 面の変数を新面にコピーすることで1つの変数を2つの変数に分割できない。一 つの変数を2つの変数に分割することは論理変数の意味を壊してしまう。
そこで、共有メモリ領域の新しい変数を生成し、それと旧面の変数を単一化を許し、 旧面の変数の値は新しい変数へのポインターにしてしまう(このポインターは実質 的に前方参照ポインタとなる)。
前方参照ポインタが使用できないために、GCノードは古い面のコピーデータが新しい 面のどこにあるのかがわからない。(古い面のデータが新しい面にコピーされた かどうかさえわからない)ゆえに、旧面データ生きているパスの数だけのコピー が行われる。その数は非常に大きな数になる恐れがある。 別の前方参照テーブルを設ける方法も考えられるが、余分な空間を必要とすこ とや実行時のオーバヘッドを考えると非現実的である。
file=shm-bottomup-copy1.eps,scale=0.7
file=shm-bottomup-copy2.eps,scale=0.7 (b) |
この方式でも、重複コピーの問題をはらんでいる。特にループしているデータに 出合うと無限ループに陥る。しかし、重複コピーする度合はそれ程多くない。 旧面読み出し専用の場合と違って、参照中のポインタの数だけの重複コピーしか起 こり得ない。(生きているパスの数と比べれば、はるかに小さい数である。)
幸い多くのKL1 プログラムはループ構造を作っていない。また複数のポインタ から参照されるデータ構造の割合も大きくない。
この方式の問題は、(前方参照タグ方式と比較して)間接ポインタと前方参照ポイン タ見分けが付かないことである。これら2者を識別しなければならない。我々の実装 では共有ヒープ上にポインタを作る場合は以下のような場合に限られている。
(1)の場合、旧面から新面の共有変数へのポインタなら前方 参照ポインタと区別ができなくなる。そのようなポインタは通常処理ノードが局所メ モリ上のネストした構造体をコピーするときに生まれる。ゆえに実行時のコピールー チンでそのようなポインタができないように間接ポインタを差し入れるように修正し た(この措置により通常処理に若干のオーバヘッドが加わっている)。
この方式で更なる問題がもう一つある。新面のレコードから旧面を指すポインタの 存在である。新面に移った通常処理ノードは、直接GC中のそのようなポインタ を参照してしまうことはないのだが、旧面を参照中の別の通常処理ノードからその ようなポインタを受け取ってしまうことがある。ゆえに、この方 式では面を再利用する時、再利用する面を参照しているポインタを新面へのポインタ にするための局所GCが必要である。