29.2 ガーベジコレクションの影響
タグが1である型のデータは, SML#のメモリ管理機構の影響を受ける. 以下,これらのデータをboxedデータと呼ぶ. boxedデータを保持するメモリ領域は,そのデータのコンストラクタが 評価されたときに暗黙に割り当てられ,SML#のガーベジコレクションによって 自動的に解放される. boxedデータをC関数に渡す場合,そのデータの内容だけでなく, そのデータを保持するメモリ領域が解放されるタイミングにも注意する必要がある. boxedデータは,Cプログラムから見て以下の性質を持つ.
-
1.
boxedデータは, Cのmalloc関数で確保したメモリ領域と同様に, 一度メモリ上に確保されると, 解放されるまでそのアドレスは変化しない. 従って,SML#の配列やrefなど, SML#プログラムから見て一意性を持つ書き換え可能なデータは, C関数から見ても同一の一意性を持つ. 組型などもメモリ上を移動しないため, 一度C関数が組型のboxedデータへのポインタを取得したならば, そのboxedデータが生きている限り,そのデータへのポインタは不変である.
-
2.
SML#プログラムからC関数を呼び出す際に引数として渡された boxedデータは,原則としてそのままC関数に渡される. C関数呼び出しに伴うデータの変換,再確保,再生成などは行われない. 従って,arrayやrefなどの書き換え可能なデータを C関数で書き換えた場合, その書き換えの効果をSML#プログラムから観測することができる.
-
3.
C関数の引数として渡されたboxedデータは, 呼び出したC関数の実行が終了するまでの間は解放されない. C関数の実行が終了しSML#プログラムに制御が戻ったとき, そのboxedデータがSML#プログラムから参照可能でなければ, そのboxedデータは解放される.
-
4.
C関数にコールバック関数として渡されたSML#の関数は, プログラムの終了まで解放されない. 従って,C関数は,受け取ったSML#関数への関数ポインタを グローバル変数などに保存し,関数呼び出しを超えて呼び出すことができる. また,異なるスレッドからコールバック関数が呼び出されたとしても, コールバック関数の自由変数の値は必ず保存されている. 一方,この仕様のため,SML#プログラムでは, コールバック関数を取るC関数の呼び出しがメモリリークを起こす可能性がある ことに注意しなければならない. SML#コンパイラは, トップレベルで定義された関数のみをコールバック関数として使用する 場合,メモリリークが起こらないことを保証する.
-
5.
SML#のガベージコレクタは正確なガベージコレクタである. boxedデータへのポインタがCのヒープ領域に 保存され,Cプログラムからそのデータが参照可能であったとしても, そのデータがSML#プログラムから参照できなければ, ガーベジコレクタはそのboxedデータを解放する. そのため,C関数は, SML#プログラムから受け取ったboxedデータへのポインタを, グローバル変数やmalloc関数で確保した領域などに保存し, SML#からのC関数呼び出しを越えて boxedデータへのポインタを保持してはならない. なお,上述の通り,C関数が受け取ったboxedデータおよびそ こから到達可能な全てのboxedデータは,そのC関数の実行が終了する までの間は移動も解放もされないため,boxedデータへのポインタを レジスタやスタックフレームに保存することは問題なく可能である.