一言で言えば
ファイナライズは、ガベージコレクターによって処理される単純な問題ではありません。参照カウントGCでの使用は簡単ですが、このGCファミリは不完全な場合が多く、一部のオブジェクトと構造の破壊とファイナライズの明示的なトリガーによってメモリリークを補償する必要があります。ガベージコレクターのトレースははるかに効果的ですが、未使用のメモリを識別するのとは対照的に、ファイナライズおよび破棄するオブジェクトを識別するのがはるかに難しくなります。したがって、時間とスペースのコスト、および実装。
前書き
あなたが求めているのは、コメントで示されているように、ガベージコレクションされた言語がガベージコレクションプロセス内で破棄/最終化を自動的に処理しない理由だと思います。
これらの言語がメモリを管理する価値のある唯一のリソースと見なしていることは非常に欠けていると思います。ソケット、ファイルハンドル、アプリケーションの状態はどうですか?
私はkdbanmanによって与えられた受け入れられた答えに同意しません。参照カウントに強く偏っているものの、事実はほとんど正しいと述べていますが、質問で不平を言っている状況を適切に説明しているとは思いません。
その答えで開発された用語が多くの問題であるとは思わず、物事を混乱させる可能性が高くなります。実際、提示されているように、用語の大部分は、手順の実行ではなく、手順のアクティブ化方法によって決定されます。ポイントは、すべての場合において、不要なオブジェクトを何らかのクリーンアッププロセスでファイナライズし、使用していたリソースを解放する必要があるということです。メモリはそれらの1つにすぎません。理想的には、ガベージコレクターを使用して、オブジェクトが使用されなくなったときにすべて自動的に実行する必要があります。実際には、GCが欠落しているか不足している可能性があります。これは、ファイナライズおよびレクラメーションのプログラムによる明示的なトリガーによって補償されます。
プログラムによる明示的なトリガーは、使用中のオブジェクトが明示的に終了されている場合、プログラミングエラーの分析が困難になる可能性があるため、問題です。
したがって、自動ガベージコレクションに依存してリソースを再利用することをお勧めします。ただし、次の2つの問題があります。
一部のガベージコレクション手法では、リソースの完全な再利用を妨げるメモリリークが許可されます。これは、参照カウントGCでよく知られていますが、一部のデータ組織を注意せずに使用する場合、他のGCテクニックで表示される場合があります(ここでは説明しません)。
GCテクニックは使用されなくなったメモリリソースの識別に優れている場合がありますが、そこに含まれるオブジェクトのファイナライズは単純ではない可能性があります。
最後に、忘れられがちな重要な点は、適切なフックが提供され、GCサイクルのコストが価値があると考えられる場合、メモリ不足だけでなく、あらゆるものによってGCサイクルがトリガーされることです。したがって、何らかのリソースが不足しているときに、一部のリソースを解放することを期待して、GCを開始してもまったく問題ありません。
参照カウントガベージコレクター
参照カウントは、サイクルを適切に処理しない弱いガベージコレクション手法です。確かに、メモリを回収するのが弱いという理由だけで、古い構造を破壊し、他のリソースを回収することに弱いでしょう。ただし、参照カウントガベージコレクター(GC)でファイナライザーを使用すると最も簡単に使用できます。参照カウントGCは、参照カウントが0になったときに構造を再利用します。または動的に。したがって、適切なファイナライザを適用し、すべてのポイントされたオブジェクトでプロセスを再帰的に呼び出した後(おそらくファイナライズプロシージャを介して)、メモリを再利用できます。
要するに、ファイナライズはRef Counting GCを使用して簡単に実装できますが、実際には循環構造に起因するそのGCの「不完全性」に悩まされます。つまり、参照カウントを使用すると、メモリは、ソケット、ファイルハンドルなどの他のリソースとまったく同じように不十分に管理されます。
実際、Ref Count GCがループ構造を再利用できないことは(一般的に)メモリリークと見なされる場合があります。すべてのGCがメモリリークを回避することは期待できません。これは、GCアルゴリズムと、動的に利用可能な型構造情報(たとえば、保守的なGC)に依存します
。
ガベージコレクターのトレース
このようなリークのないGCのより強力なファミリは、明確に識別されたルートポインタから開始して、メモリのライブ部分を探索するトレースファミリです。このトレースプロセスでアクセスされないメモリのすべての部分(実際にはさまざまな方法で分解できますが、単純化する必要があります)は、メモリの未使用部分であり、再利用できます1。これらのコレクターは、プログラムが何をするかに関係なく、プログラムがアクセスできなくなったすべてのメモリー部分を再利用します。循環構造を再利用し、より高度なGCは、このパラダイムのバリエーションに基づいており、時には高度に洗練されています。場合によっては参照カウントと組み合わせて、その弱点を補うことができます。
問題は、あなたの声明(質問の最後)です:
自動ガベージコレクションを提供する言語は、オブジェクトが使用されなくなったときに100%の確実性でわかるように、オブジェクトの破壊/最終化をサポートする主要な候補のようです。
コレクタのトレースでは技術的に正しくありません。
100%の確実性で知られているのは、メモリのどの部分が使用されなくなったかです。(より正確には、プログラムのロジックに従って使用できなくなった一部の部分は、プログラム内にそれらへの無意味なポインターがある場合、まだ使用中と見なされるため、アクセスできなくなっていると言われるべきですデータ。)しかし、メモリのこれらの現在未使用の部分に格納されている未使用のオブジェクトを知るには、さらなる処理と適切な構造が必要です。これは、プログラムがメモリのこれらの部分に接続されていないため、プログラムの既知の情報から判断することはできません。
したがって、ガベージコレクションのパスの後、使用されなくなったオブジェクトを含むメモリのフラグメントが残りますが、正しいファイナライズを適用するためにこれらのオブジェクトが何であるかを先験的に知る方法はありません。さらに、トレースコレクターがマークアンドスイープタイプの場合、フラグメントの一部には、以前のGCパスで既にファイナライズされているが、フラグメント化の理由で使用されなかったオブジェクトが含まれている可能性があります。ただし、これは拡張明示的な型指定を使用して処理できます。
単純なコレクターはこれらのメモリの断片を回収するだけで、苦労することなく、ファイナライズには特定のパスを使用してその未使用メモリを探索し、そこに含まれるオブジェクトを識別し、ファイナライズ手順を適用します。ただし、そのような調査では、そこに格納されているオブジェクトのタイプを決定する必要があり、適切なファイナライズがある場合は、タイプ決定も必要です。
そのため、GC時間の追加コスト(追加パス)と、場合によっては追加のメモリコストにより、そのパス中にさまざまな手法で適切な型情報を使用できるようになります。時間とスペースのオーバーヘッドはすべてのオブジェクトに関係する可能性がありますが、少数のオブジェクトのみをファイナライズしたい場合が多いため、これらのコストは重要です。
もう1つのポイントは、GCの実行だけでなく、時間とスペースのオーバーヘッドがプログラムコードの実行に関係する可能性があることです。
私はあなたがリストした言語の多くの詳細を知らないので、特定の問題を指してより正確な答えを出すことはできません。Cの場合、タイピングは保守的なコレクターの開発につながる非常に難しい問題です。私の推測では、これはC ++にも影響しますが、私はC ++の専門家ではありません。これは、保守的なGCに関する多くの研究を行ったハンス・ベームによって確認されているようです。保守的なGCでは、データの正確な型情報が不足している可能性があるため、未使用のメモリをすべて体系的に再利用できません。同じ理由で、ファイナライズ手順を体系的に適用することはできません。
そのため、いくつかの言語からわかるように、あなたが求めていることを行うことができます。しかし、無料ではありません。言語とその実装によっては、機能を使用しない場合でもコストがかかる場合があります。これらの問題に対処するには、さまざまな手法とトレードオフを考慮することができますが、それは合理的なサイズの答えの範囲を超えています。
1-これは、トレースコレクションの抽象的な表現(コピーとマークアンドスイープGCの両方を含む)です。トレースコレクターのタイプによって異なり、コピーまたはマークのどちらであるかによって、メモリの未使用部分の探索は異なります。スイープが使用されます。
finalize
/destroy
は嘘ですか?実行される保証はありません。そして、たとえそうだとしても、いつ(自動ガベージコレクションが行われたか)がわからず、必要なコンテキストがまだ残っている(既に収集されている可能性がある)したがって、他の方法で一貫した状態を確保する方が安全であり、プログラマーにそうすることを強制したい場合があります。