文書の出典
- 出典
- Lua 5.5.1 Documentation
- 上流バージョン
- Lua 5.5.1
- 文書状態
- 翻訳
2.5 – ガベージコレクション
Luaは自動メモリ管理を行う。これは、新しいオブジェクトへのメモリ割り当てや、不要になったオブジェクトの解放を気にする必要がないことを意味する。Luaは、すべての死んだオブジェクトを収集するガベージコレクターを実行して、メモリを自動的に管理する。文字列、テーブル、ユーザーデータ、関数、スレッド、内部構造など、Luaが使用するすべてのメモリが自動管理の対象となる。
プログラムの通常実行中にオブジェクトへ再びアクセスしないとコレクターが確信できた時点で、そのオブジェクトは死んでいるとみなされる。(ここでいう「通常実行」には、死んだオブジェクトを復活させるファイナライザー(§2.5.3を参照)と、デバッグライブラリを使う一部の操作を含まない。)オブジェクトが死んだとコレクターが確信できる時点は、プログラマーの予想と一致しない場合があることに注意すること。保証されるのは、プログラムの通常実行中にまだアクセスされる可能性があるオブジェクトをLuaが収集しないことと、Luaからアクセス不能なオブジェクトを最終的には収集することだけである。(ここでLuaからアクセス不能とは、変数も他の生きたオブジェクトもそのオブジェクトを参照していないことを意味する。)LuaはCコードについて何も知らないため、レジストリ(§4.3を参照)を介してアクセスできるオブジェクトを収集することはない。これには、グローバル環境(§2.2を参照)とメインスレッドが含まれる。
Luaのガベージコレクター(GC)は、インクリメンタルと世代別という2つのモードで動作できる。
デフォルトパラメーターを使うデフォルトGCモードは、ほとんどの用途に適している。ただし、メモリの割り当てと解放に多くの時間を費やすプログラムは、別の設定から利益を得られる場合がある。GCの動作は、プラットフォーム間でも異なるLuaリリース間でも移植性がなく、最適な設定にも移植性がないことに留意すること。
Cではlua_gc、Luaではcollectgarbageを呼び出すことで、GCのモードとパラメーターを変更できる。これらの関数は、コレクターの停止や再開など、コレクターを直接制御するためにも使用できる。
2.5.1 – インクリメンタルガベージコレクション
インクリメンタルモードでは、各GCサイクルが、プログラムの実行と交互に行われる小さなステップでマークアンドスイープ収集を実行する。このモードでは、コレクターはガベージコレクターの一時停止、ガベージコレクターのステップ乗数、ガベージコレクターのステップサイズという3つの数値でガベージコレクションサイクルを制御する。
ガベージコレクターの一時停止は、コレクターが新しいサイクルを開始するまでの待機時間を制御する。前回の収集後の合計に対してバイト数が*n%*に達すると、コレクターは新しいサイクルを開始する。値が大きいほどコレクターの積極性は低くなる。100以下の値では、コレクターは新しいサイクルの開始を待機しない。値200では、合計バイト数が2倍になるまで待機してから新しいサイクルを開始する。
ガベージコレクターのステップサイズは、各インクリメンタルステップの大きさ、具体的にはインタープリターがステップを実行する前に割り当てるバイト数を制御する。値nは、インタープリターが各ステップの間におよそnバイトを割り当てることを意味する。
ガベージコレクターのステップ乗数は、各インクリメンタルステップが行う作業量を制御する。値nは、割り当てられた各ワードに対してインタープリターがn%の作業単位を実行することを意味する。1作業単位は、おおよそ1スロットの走査または1オブジェクトのスイープに相当する。値が大きいほどコレクターは積極的になる。値が小さすぎると、コレクターが遅すぎてサイクルを完了できなくなる場合があるので注意すること。特別な場合として、値0は作業量に上限がないことを意味し、実質的に非インクリメンタルなstop-the-worldコレクターとなる。
2.5.2 – 世代別ガベージコレクション
世代別モードでは、コレクターは最近作成されたオブジェクトだけを走査するマイナー収集を頻繁に行う。マイナー収集後のバイト数が制限を超えている場合、すべてのオブジェクトを走査するメジャー収集へ移行する。その後、プログラムがマイナー収集へ戻るのに十分なガベージを生成していると検出するまで、コレクターはメジャー収集を続ける。
世代別モードは、マイナー乗数、マイナー・メジャー乗数、メジャー・マイナー乗数という3つのパラメーターを使用する。
マイナー乗数は、マイナー収集の頻度を制御する。マイナー乗数をxとすると、バイト数が前回のメジャー収集直後の使用量より*x%*増えたとき、新しいマイナー収集が実行される。たとえば乗数20では、バイト数が前回のメジャー収集後の合計より20%増えると、コレクターがマイナー収集を行う。
マイナー・メジャー乗数は、メジャー収集への移行を制御する。乗数をxとすると、古いオブジェクトのバイト数が前回のメジャー収集後の合計より*x%*増えたとき、コレクターはメジャー収集へ移行する。たとえば乗数100では、古いバイト数が前回のメジャー収集後の合計の2倍より大きくなると、コレクターがメジャー収集を行う。特別な場合として、値0はコレクターによるメジャー収集を停止する。
メジャー・マイナー乗数は、マイナー収集へ戻る移行を制御する。乗数をxとすると、メジャー収集が前回のサイクル中に割り当てられたバイトの少なくとも*x%*を収集した後、コレクターはマイナー収集へ戻る。特に乗数0では、1回のメジャー収集を行った直後にマイナー収集へ戻る。
2.5.3 – ガベージコレクションのメタメソッド
テーブルと、C APIを使用する場合はフルユーザーデータに、ガベージコレクターのメタメソッドを設定できる(§2.4を参照)。ファイナライザーと呼ばれるこれらのメタメソッドは、対応するテーブルまたはユーザーデータが死んでいるとガベージコレクターが検出したときに呼び出される。ファイナライザーを使うと、ファイル、ネットワーク接続、データベース接続のクローズや、独自メモリの解放など、Luaのガベージコレクションと外部リソースの管理を連携できる。
オブジェクト(テーブルまたはユーザーデータ)を収集時にファイナライズするには、ファイナライズ対象としてマークしなければならない。オブジェクトへメタテーブルを設定し、そのメタテーブルが__gcメタメソッドを持っているとき、オブジェクトはファイナライズ対象としてマークされる。__gcフィールドを持たないメタテーブルを設定した後で、そのメタテーブルにフィールドを作成しても、オブジェクトはファイナライズ対象としてマークされないことに注意すること。
マークされたオブジェクトが死んでも、ガベージコレクターは直ちに収集しない。代わりに、Luaはそのオブジェクトをリストへ入れる。収集後、Luaはそのリストを走査する。リスト内の各オブジェクトについて、オブジェクトの__gcメタメソッドを調べ、存在すればそのオブジェクトだけを引数として呼び出す。
各ガベージコレクションサイクルの終わりに、そのサイクルで収集されたオブジェクトのファイナライザーは、オブジェクトがファイナライズ対象としてマークされた順序と逆の順序で呼び出される。すなわち、最初に呼び出されるファイナライザーは、プログラム内で最後にマークされたオブジェクトに関連付けられたものである。各ファイナライザーは、通常のコードの実行中の任意の時点で実行される可能性がある。
収集中のオブジェクトをファイナライザーがまだ使用する必要があるため、そのオブジェクト(およびそのオブジェクトを介してだけアクセスできる他のオブジェクト)はLuaによって復活させられなければならない。通常、この復活は一時的であり、次のガベージコレクションサイクルでオブジェクトのメモリが解放される。ただし、ファイナライザーがオブジェクトを何らかのグローバルな場所(たとえばグローバル変数)へ格納した場合、復活は恒久的となる。さらに、ファイナライザーがファイナライズ中のオブジェクトを再びファイナライズ対象としてマークした場合、次にそのオブジェクトが死んでいるサイクルでファイナライザーが再び呼び出される。いずれの場合も、オブジェクトのメモリは、そのオブジェクトが死んでおり、かつファイナライズ対象としてマークされていないGCサイクルでだけ解放される。
状態を閉じると(lua_closeを参照)、Luaはファイナライズ対象としてマークされたすべてのオブジェクトのファイナライザーを、マークされた順序と逆の順序で呼び出す。この段階でファイナライザーがオブジェクトを収集対象としてマークしても、そのマークには効果がない。
ファイナライザーはyieldできず、ガベージコレクターも実行できない。予測不能な時点で実行される可能性があるため、各ファイナライザーは、関連するリソースを適切に解放するために必要な最小限の処理へ制限するのがよい慣行である。
ファイナライザーの実行中に発生したエラーは警告を生成し、伝播しない。
2.5.4 – 弱参照テーブル
弱参照テーブルは、要素が弱参照であるテーブルである。ガベージコレクターは弱参照を無視する。言い換えると、あるオブジェクトへの参照が弱参照だけである場合、ガベージコレクターはそのオブジェクトを収集する。
弱参照テーブルは、弱いキー、弱い値、またはその両方を持つことができる。値が弱いテーブルでは値の収集が可能だが、キーの収集は妨げられる。キーと値の両方が弱いテーブルでは、両方を収集できる。いずれの場合も、キーまたは値のどちらかが収集されると、その対全体がテーブルから削除される。テーブルの弱さは、そのメタテーブルの__modeフィールドによって制御される。このメタ値が存在する場合、次の文字列のいずれかでなければならない。キーが弱いテーブルは「k」、値が弱いテーブルは「v」、キーと値の両方が弱いテーブルは「kv」。
キーが弱く値が強いテーブルは、エフェメロンテーブルとも呼ばれる。エフェメロンテーブルでは、キーへ到達可能な場合にだけ、値へ到達可能であるとみなされる。特に、キーへの参照がその値を介したものだけである場合、その対は削除される。
テーブルの弱さに対する変更は、次の収集サイクルで初めて有効になる場合がある。特に、弱さをより強いモードへ変更した場合、その変更が有効になる前に、Luaがそのテーブルから一部の項目を収集する可能性がある。
明示的に構築されるオブジェクトだけが弱参照テーブルから削除される。数値や軽量C関数のような値はガベージコレクションの対象ではないため、関連する値が収集される場合を除き、弱参照テーブルから削除されない。文字列はガベージコレクションの対象だが、明示的な構築を持たず、値によって等価性が決まるため、オブジェクトよりも値のように動作する。したがって、弱参照テーブルから削除されない。
復活したオブジェクト(すなわち、ファイナライズ中のオブジェクトと、ファイナライズ中のオブジェクトを介してだけアクセスできるオブジェクト)は、弱参照テーブル内で特別な動作をする。これらはファイナライザーを実行する前に弱い値から削除されるが、弱いキーからは、ファイナライザー実行後の次の収集、すなわち実際に解放されるときに初めて削除される。この動作により、ファイナライザーは弱参照テーブルを介してオブジェクトに関連付けられたプロパティへアクセスできる。
弱参照テーブル自体が収集サイクルで復活したオブジェクトに含まれる場合、次のサイクルまで適切にクリアされないことがある。