5

チャットでのディスカッションの過程で、このコンソール アプリケーションを作成しました。

コード:

using System;

class Program
{
    static void Main(string[] args)
    {
        CreateClass();
        Console.Write("Collecting... ");
        GC.Collect();
        Console.WriteLine("Done");
    }

    static void CreateClass()
    {
        SomeClass c = new SomeClass();
    }
}

class SomeClass
{
    ~SomeClass()
    {
        throw new Exception();
    }
}

結果:

Collecting... Done

Unhandled Exception: System.Exception: Exception of type 'System.Exception' was
thrown.
   at SomeClass.Finalize()

印刷される前 に、アプリがクラッシュすることを期待していました。Done

作り方はあまり気にしません。私の質問は、なぜそうしないのですか?

4

4 に答える 4

12

ファイナライザーを含むオブジェクトは、単一のガベージコレクション手順内で収集することはできません。このようなオブジェクトはf-reachableキューに移動され、ファイナライザーが呼び出されるまでそこに残ります。その後、ガベージコレクションを行うことができます。

次のコードの方が優れていますが、とにかくそれに依存しないでください。

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

また、ファイナライザーで例外をスローすることは、テスト目的であっても、私にはあまりにも残酷に思えます。

また、ファイナライザーの興味深い副作用:ファイナライザーにthis参照を格納する(静的変数に割り当てる)場合、ファイナライザーを持つオブジェクトはそれ自体を「復活」させることができます(効果的にそれ自体のガベージコレクションを防ぐ)。

于 2012-07-05T23:55:41.653 に答える
6

ドキュメントを読みましたか?

このメソッドを使用して、アクセスできないすべてのメモリを再利用しようとします。

これはコマンドではなく、要求であり、思いどおりに機能する場合とそうでない場合があります。いずれにせよ、これはあまり良い考えではありません (プロセスの結果として、非常に多くの小さな存続期間の短いオブジェクトが作成されることがありますGC.Collect

実際の問題を解決しようとしているようには見えず、代わりに GC をいじっているように見えるので、これは私が提供しなければならない最善のアドバイスです。

于 2012-07-05T23:53:14.240 に答える
3

最も一般的なガベージ コレクターの実装では、ガベージ コレクション サイクル中にマネージ ユーザー コードを実行することはできません。 Finalizeメソッドはユーザー コードとしてカウントされます。メソッドの実行中にシステムが他のすべてのユーザー コードをフリーズすることは理論的には可能ですがFinalize、この動作により、マルチコア システムでのガベージ コレクションの明らかなコストが増加し、デッドロックの可能性も増加します。これらの問題を回避するために、システムはFinalizeガベージ コレクションの一部としてメソッドを実行しませんが、代わりに、それらのメソッドを保持する必要があるオブジェクトのリストを構築します。Finalizeメソッドが実行されます (リストは「freachable キュー」と呼ばれます)。リスト自体はルート化された参照と見なされるため、freachable キュー内のオブジェクトによって参照されるオブジェクトは、少なくともシステムがキューから freachable オブジェクトを取得し、そのFinalizeメソッドを実行するまで、強くルート化されていると見なされます。参照を破棄します。

ファイナライズに関する Microsoft の初期のドキュメントは非常に紛らわしいものです。ファイナライズ可能なオブジェクトが参照を保持しているオブジェクトは、それらのメソッドが実行されたときに存在しない可能性があることを示しているからです。実際、そのようなオブジェクトはすべて存在することが保証されています。不確実なのは、彼らがすでにFinalizeメソッドを実行しているかどうかです。

于 2012-07-06T15:11:16.923 に答える
0
  1. GC.Collect() は、参照されず、ファイナライザー メソッドを持つオブジェクトをファイナライザー キューに入れます。
  2. ファイナライズ メソッドを呼び出してファイナライザー キューをクリアします。

上記の 2 つの点が明確であれば、コードは静的メソッドで SomeClass への参照を保持しています。これは、プログラムのメイン メソッドが実行されるまで、まだ生きていることを意味します。

「完了」を出力する前にアプリをクラッシュさせたい場合は、まず SomeClass オブジェクトを無効にしてから GC.Collect を呼び出します。オブジェクトをファイナライザーキューに入れますが、そのキューをいつクリアするかはGCの願いです。GC でそのキューをクリアしてファイナライザーを呼び出す場合は、GC.WaitForPendingFinalizers() を呼び出します。スレッドは、ファイナライザーが呼び出されるまで待機し、その後続行します。目的の出力に合わせてコードを変更しました。例外をスローする代わりに、ファイナライザーでステートメントを出力しました。

class Program
    {
        static void Main(string[] args)
        {
            CreateClass();
            Console.Write("Collecting... ");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine("Done");

            Console.ReadLine();
        }

        static void CreateClass()
        {
            SomeClass c = new SomeClass();
            c = null;
        }
    }

    class SomeClass
    {
        ~SomeClass()
        {
            Console.WriteLine("Finalized...");
        }
    }
于 2013-10-09T11:43:59.677 に答える