45

わかりました。GCはFinalize、オブジェクトをガベージとして識別するときに、オブジェクトのメソッドを暗黙的に呼び出すことが知られています。しかし、私がやったらどうなりGC.Collect()ますか?ファイナライザーはまだ実行されていますか?ばかげた質問かもしれませんが、誰かが私にこれを尋ね、私は「はい」と答えて、「それは完全に正しいですか?」と思いました。

4

5 に答える 5

75

わかりました。GC は、そのオブジェクトをガベージとして識別したときに、そのオブジェクトに対して Finalize メソッドを暗黙的に呼び出すことが知られています。

ダメダメダメ。知識であるためには、ステートメントが真実でなければならないため、それは知られていません。その声明は誤りです。ガベージ コレクタは、それ自体が実行されるか、ユーザーが を呼び出すかに関係なく、トレース時にファイナライザを実行しませんファイナライザ スレッドは、トレース コレクタがガベージを見つけた後にファイナライザを実行し、これは への呼び出しに関して非同期に行われます。(別の回答が指摘しているように、まったく発生しない場合があります。)つまり、から制御が戻る前にファイナライザースレッドが実行されることに依存することはできません。CollectCollectCollect

これがどのように機能するかを単純化しすぎたスケッチを次に示します。

  • コレクションが発生すると、ガベージ コレクターのトレース スレッドがルート (生きていることがわかっているオブジェクト、それらが参照するすべてのオブジェクトなど) をトレースして、死んだオブジェクトを特定します。
  • 保留中のファイナライザーを持つ「デッド」オブジェクトは、ファイナライザー キューに移動されます。ファイナライザー キューは rootです。したがって、これらの「死んだ」オブジェクトは実際にはまだ生きています。
  • 通常、GC トレース スレッドとは異なるスレッドであるファイナライザー スレッドは、最終的に実行され、ファイナライザー キューを空にします。その後、これらのオブジェクトは完全に無効になり、トレース スレッドの次のコレクションで収集されます。(もちろん、彼らは最初のコレクションを生き延びたばかりなので、より高い世代にある可能性があります。)

先ほど言ったように、これは単純化しすぎています。ファイナライザー キューの動作の正確な詳細は、それよりも少し複雑です。しかし、それはアイデアを十分に伝えます。ここでの実際的な結果は、呼び出しが finalizers も実行すると想定できないことですCollect。もう一度繰り返します。ガベージ コレクターのトレース部分はfinalizersを実行せCollect、コレクション メカニズムのトレース部分のみを実行します。

すべてのファイナライザーが実行されたことを保証したい場合はWaitForPendingFinalizers、呼び出し後に適切な名前のを呼び出します。Collectこれにより、ファイナライザー スレッドがキューを空にするまで、現在のスレッドが一時停止します。そして、それらのファイナライズされたオブジェクトのメモリが再利用されていることを確認したい場合は、もう一度呼び出す必要がCollectあります

もちろん、デバッグとテストの目的でのみこれを行うべきであることは言うまでもありません。本当に、本当に正当な理由がない限り、このナンセンスを本番環境のコードで実行しないでください。

于 2012-12-19T15:03:45.163 に答える
15

実際には「場合による」という答え。実際には、すべてのファイナライザーを実行する専用スレッドがあります。つまり、 への呼び出しGC.Collectのみがこのプロセスをトリガーし、すべてのファイナライザーの実行は非同期で呼び出されます。

すべてのファイナライザーが呼び出されるまで待ちたい場合は、次のトリックを使用できます。

GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();
于 2012-12-19T14:56:25.697 に答える
10

はい、すぐにはできません。この抜粋は、ガベージ コレクション: Microsoft .NET Framework の自動メモリ管理 (MSDN マガジン) (*)からのものです。

「アプリケーションが新しいオブジェクトを作成すると、new 演算子がヒープからメモリを割り当てます。オブジェクトの型に Finalize メソッドが含まれている場合、オブジェクトへのポインターがファイナライズ キューに配置されます。ファイナライズ キューは、制御される内部データ構造です。ガベージ コレクターによって キュー内の各エントリは、オブジェクトのメモリを再利用する前に Finalize メソッドを呼び出す必要があるオブジェクトを指します。

GC が発生すると、ガベージ コレクターはファイナライズ キューをスキャンして、これらのオブジェクトへのポインターを探します。ポインターが見つかると、そのポインターはファイナライズ キューから削除され、freachable キュー (「F-reachable」と発音) に追加されます。freachable キューは、ガベージ コレクターによって制御される別の内部データ構造です。freachable キュー内の各ポインターは、Finalize メソッドを呼び出す準備ができているオブジェクトを識別します。

Finalize メソッドの呼び出し専用の特別なランタイム スレッドがあります。freachable キューが空の場合 (通常はそうです)、このスレッドはスリープします。ただし、エントリが表示されると、このスレッドが起動し、各エントリをキューから削除して、各オブジェクトの Finalize メソッドを呼び出します。このため、Finalize メソッドでは、コードを実行しているスレッドについて何らかの仮定を行うコードを実行しないでください。たとえば、Finalize メソッドでスレッド ローカル ストレージにアクセスすることは避けてください。」

(*) 2000 年 11 月以降なので、変更されている可能性があります。

于 2012-12-19T14:55:22.833 に答える
5

ガベージが収集されると (メモリ プレッシャまたは に応答してGC.Collect())、ファイナライズが必要なオブジェクトがファイナライズ キューに入れられます。

を呼び出さない限りGC.WaitForPendingFinalizers()、ファイナライザーは、ガベージ コレクションが終了した後もずっとバックグラウンドで実行し続ける可能性があります。


ところで、ファイナライザーがまったく呼び出されるという保証はありませMSDNから...

次の例外的な状況では、Finalize メソッドが最後まで実行されないか、まったく実行されないことがあります。

  • 別のファイナライザーは無期限にブロックします (無限ループに入り、取得できないロックを取得しようとするなど)。ランタイムはファイナライザーを最後まで実行しようとするため、ファイナライザーが無期限にブロックされると、他のファイナライザーが呼び出されない可能性があります。
  • プロセスは、ランタイムにクリーンアップの機会を与えずに終了します。この場合、プロセス終了のランタイムの最初の通知は、DLL_PROCESS_DETACH 通知です。

ファイナライズ可能なオブジェクトの数が減少し続けている間のみ、ランタイムはシャットダウン中にオブジェクトをファイナライズし続けます。

于 2012-12-19T14:57:58.940 に答える
0

ここでさらにいくつかの点を述べる価値があります。

ファイナライザーは、.net オブジェクトが管理されていないリソースを解放できる最後のポイントです。ファイナライザーは、インスタンスを正しく破棄しない場合にのみ実行されます。多くの場合、ファイナライザを実行しないことが理想的です。適切な破棄の実装ではファイナライズを抑制する必要があるためです。

これは正しい IDispoable 実装の例です

破棄可能なオブジェクトの Dispose メソッドを呼び出すと、すべての参照がクリアされ、ファイナライズが抑制されます。Dispose メソッドを呼び出すのを忘れるあまり上手でない開発者がいる場合、Finalizer は命の恩人です。

于 2016-01-31T22:54:14.273 に答える