Skip to content

ファイナライゼーション

概要

Duktapeは、カスタム機能としてオブジェクトのファイナライゼーションをサポートしています。ファイナライザーは、オブジェクトが解放されようとするときに呼び出され、アプリケーション・コードが、例えば、オブジェクトに関連するネイティブ・リソースを解放できるようにします。ファイナライザーは、ECMAScript関数またはDuktape/C関数のいずれかにすることができます。しかし、ECMAScript のファイナライザはスクリプトのタイムアウトと悪い相互作用をする可能性があります。

例については、ファイナライザの使用方法を参照してください。

現在のファイナライザーを取得・設定する

プロトタイプ・チェーンに(あるいはオブジェクト自体に)内部 _Finalizer プロパティを持つオブジェクトは、解放される前にファイナライズの対象となります。内部プロパティは直接アクセスしてはいけませんが、以下を使用して読み書きが可能です。

  • Duktape.fin(obj) (ECMAScript) または duk_get_finalizer() (C) は、現在のファイナライザを取得します。
  • Duktape.fin(obj, fn) (ECMAScript) または duk_set_finalizer() (C) は、現在のファイナライザを設定します。

ファイナライザー関数の引数と戻り値

ファイナライザー関数は、2つの引数で呼び出されます。

  • ファイナライズされるオブジェクト。
  • オブジェクトがヒープ破壊の一部として強制的に解放されるかどうかを示す boolean フラグ。この引数は Duktape 1.4.0 で追加されました。
  • false (通常の場合)の場合、ファイナライザは、戻る前にオブジェクトへのライブ参照を作成することによってオブジェクトを救出することができ、ファイナライザは後で再び呼ばれることが保証されています(遅くともヒープ破壊)。
  • true (ヒープ破棄時の強制終了) の場合、オブジェクトは救出できず、ファイナライザが終了した後に強制的に解放されます。ネイティブなリソースは、ファイナライザへのさらなる呼び出しを期待せずに解放する必要があります。

ファイナライザーの戻り値は無視されます。ファイナライザがスローしたエラーも、静かに無視されます。

ファイナライザ実行保証

主なファイナライザーの保証は以下の通りです。

  • ファイナライザは、参照カウントやマーク・アンド・スイープで検出された到達不能な オブジェクトに対して実行されます。しかし、ファイナライザはすぐに実行されるわけではなく、参照カウントによって オブジェクトが到達不可能になったことが検出されても実行されません。
  • ファイナライザーは、Duktapeヒープが破壊されたときにも、到達可能な状態に関わらず、残りの全てのオブジェクトに対して実行されます。
  • ファイナライザーは、オブジェクトが再び到達可能になることで救済されない限り、遅くともヒープが破壊された時点で一度だけ呼び出されます。オブジェクトは、それ自身のファイナライザによって救済されることもあれば、 マーク・アンド・スイープでオブジェクトのセットをファイナライズする際に、他のオ ブジェクトのファイナライザによって救済されることもあります。たとえば、X.ref = Y で、X と Y の両方が到達不能になった場合、Y のファイナライザが実行され、後で X のファイナライザが X と Y の両方を救済することが可能です。
  • オブジェクトは任意の回数だけ救出される可能性があります。ファイナライザは「救出サイクル」ごとにちょうど1回だけ呼び出されます。この保証がある場合でも、ファイナライザーはリエントラントであることがベストプラクティスであり、例えば、再入力された場合にネイティブリソースを複数回解放することは慎重に避けなければなりません。
  • ファイナライザーは Proxy オブジェクトに対しては実行されず、プレーンなターゲットオブジェクトに対して実行されます。これにより、Proxy オブジェクトが作成されるときにファイナライザーが複数回実行されることがありません。

これらを合わせると、ヒープが破壊される前のある時点でファイナライザが実行されることが保証され、これによりネイティブリソース(ソケットやファイルなど)が確実に解放されます。この保証にはいくつかの例外があり、詳細は以下を参照してください。

  • ヒープ破壊時のファイナライザーのサニティ制限により、ファイナライザーが実行されないことがあります。
  • スクリプトのタイムアウトが現在のコールスタックから伝搬される場合、ECMAScript のファイナライザーは直ちにスクリプトのタイムアウトエラーを再投与します。Duktape/Cのファイナライザーは、通常通り実行されます。
  • Duktape がファイナライザーを呼び出そうとしたときに(緊急の GC にもかかわらず)メモリ不足になった場合、呼び出しエラーは黙って無視され、ファイナライザーはスキップされます。
  • オブジェクトがマーク&スイープによってファイナライズされ、次のマーク&スイープラウンドがレスキューを検出する前に到達不可能になった場合、そのオブジェクトのファイナライザは実行されません。

Duktapeヒープが破壊されるとき、ファイナライザーの動作にはいくつかの制限があります。

  • ファイナライザーは、到達可能なオブジェクトを含む、ヒープ内の全てのファイナライズ可能なオブジェクトに対して実行されます。
  • ファイナライザーはオブジェクトを救出することはできません。「救出」のセマンティクスが曖昧になります。ファイナライザーの第2引数は、ヒープ破棄時に呼び出された場合、救助が不可能であることを示すために真になります。
  • ファイナライザーは、ファイナライズ可能な新しいオブジェクトを作成することができ、これらのオブジェクトもファイナライズされます。例えば、ファイナライザーは、オブジェクトの破壊をHTTPで通知することができます。これは、独自のファイナライザーを持つネイティブのネットワークリソースを使用することができます。しかし、暴走したファイナライザーがヒープの破壊を防ぐことができないように、このプロセスにはサニティーの制限があります。
  • ファイナライザーのサニティ・アルゴリズムはバージョンに依存します。このアルゴリズムでは、ファイナライザ可能なオブジェクトの数が最初は増加しますが、合理的な時間内に減少しなければファイナライザ処理が中断され、一部のネイティブ リソース リークが発生する可能性があります。

その他の現在の制限事項

  • スクリプト実行タイムアウト (DUK_USE_EXEC_TIMEOUT_CHECK) が使用され、タイムアウトが発生した場合、ECMAScript ファイナライザが実行を開始しても、スクリプトタイムアウトによりすぐに失敗する可能性があります。これが具体的な懸念事項である場合、代わりにDuktape/Cネイティブファイナライザを使用してください。このファイナライザは、タイムアウトが伝播しても正常に実行されます。
  • ファイナライザーを実行するコンテキスト(Duktapeスレッド)は、現在ヒープ内の任意のコルーチンである可能性があります。これはサンドボックス化において考慮されなければなりません。
  • ファイナライザーは現在、降伏することができません。