12

重複の可能性:
オブジェクト初期化子の使用における復活の違い

C#でガベージコレクターがどのように機能するかを理解するのに苦労しています(私は2012を使用しているので、C#4.5です)。これが私のサンプルコードです:

    public class A
    {
        public int c;
        public A(){}
        public A(int pC)
        {
            c = pC;
        }
    }

    public static void Main()
    {
        // Test 1
        var a = new A {c=199};
        var aRef = new WeakReference(a);
        a = null;
        Console.WriteLine(aRef.IsAlive);
        GC.Collect();
        Console.WriteLine(aRef.IsAlive);
        //            Console.WriteLine(GC.GetGeneration(aRef.Target)); //output 1

        // Test 2
        a = new A (200);
        aRef = new WeakReference(a);
        a = null;
        Console.WriteLine(aRef.IsAlive);
        GC.Collect();
        Console.WriteLine(aRef.IsAlive);
    }

出力はTrue/True / True/Falseです

どちらのテストでも、GC.Collectを呼び出す前に、ヒープ上のオブジェクトにルートがないように見えます。しかし、テスト1では、オブジェクトは強制gc実行を通過しますが、テスト2では通過しません。それで、イニシャライザーの使用について何か不思議なことが起こっていますか?私の推測では、初期化子を使用すると、同じオブジェクトの強力なルートになる「余分なコード」が存在する可能性があります。

ありがとう。

4

3 に答える 3

5

イニシャライザーを使用する場合

 var a = new A {c=199}; --------> 1

コンパイラには、オブジェクトがGCを通過するようにするスタック上の追加の参照が含まれています。
上記の(1)は次のようになります。

 var temp = new A() ;
  temp.c=199;
  var a=temp . 

この一時変数は、GC中にこのオブジェクトを存続させると思います。

このリンクを参照してください

編集TomTomがコメントで述べたように。デバッガーが実行されている場合、GCの動作が変更され、最後に使用されるまでではなく、メソッドが終了するまで変数が存続します。私が間違っている場合は訂正してください

于 2013-01-24T07:26:07.027 に答える
4

明らかに、デバッグビルドを実行しているか、デバッガーが接続されています。ガベージコレクターは、ジャストインタイムコンパイラーからライフタイムヒントを取得し、コードのどのセクションでローカル変数を参照できるかを示すテーブルを生成します。ガベージコレクタは、GCによって中断された実行メソッドのスタックをウォークし、このテーブルに対して実行場所をチェックします。そして、一致するものが見つかったときに、参照を有効としてカウントします。

コードがデバッグ構成でビルドされた場合、またはデバッガーが接続されている場合、ジッターはこのテーブルを変更し、メソッド本体の最後まで変数を存続させます。これにより、コードのデバッグがはるかに簡単になり、ローカル変数を監視式に入れることができ、変数が使用されなくなったポイントを超えても結果が生成されます。

@Imposterによって投稿された答えは正しいです。非表示の一時変数は、Aの最初のインスタンスを存続させます。また、デバッガーを使用しているため、ガベージコレクターはメソッドが終了するまで有効であると見なします。2番目a = null;の割り当てにより、2番目のインスタンスをガベージコレクションできます。

このコードを本番環境で実行すると実際に何が起こるかは大きく異なります。1つは、ジッタオプティマイザがa=nullの割り当てを削除することです。これらの割り当てには有用な副作用がないことがわかっているため、それらの割り当てのコードは生成されません。かなり直感的ではありませんが、これを確認する最良の方法は、次の手順を実行することです。

  • コードからa=nullの割り当てを削除します
  • Build +ConfigurationManagerを使用してリリース構成に切り替えます
  • [ツール]+[オプション]、[デバッグ]、[一般]を使用して、[モジュールの読み込み時にJIT最適化を抑制する]オプションのチェックを外します。

最後のオプション変更により、ジッターがコードを生成する方法に影響を与えることなく、デバッガーを使い続けることができます。これで、一時変数はAの最初のインスタンスを参照したままにしなくなり、ジッターによって生成されたテーブルは、メソッドの最初のステートメントの有効な参照を格納するものとしてのみマークします。プログラムを実行すると、次のように表示されます。

True
False
True
False

nullへの参照を設定することは実際には必要ないという重要な新しい洞察により、ガベージコレクターはあなたが助けを必要としないほど賢いです。

于 2013-01-24T13:13:09.667 に答える
0

私は、GCルートを作成するスタック上の追加の参照を強く信じています。

SOSデバッグで確認しましょう。

  1. イニシャライザを使用する場合、

      (before GC.Collect)
      01bdc110 - object address of A.
    
      !GCRoot 01bdc110 
      Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info.
      Scan Thread 2920 OSTHread b68
      ESP:20e960:Root:  01bdc110(GCTest.A)
      **ESP:20ebb8:Root:  01bdc110(GCTest.A) -- extra stack reference generated by compiler**
      ESP:20ebbc:Root:  01bdc110(GCTest.A)
      ESP:20ebc4:Root:  01bdc110(GCTest.A)
      Scan Thread 2404 OSTHread 964
    

    (GC.Collect後)

      !GCRoot 01bdc110 
      Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info.
      Scan Thread 2920 OSTHread b68       
      **ESP:20ebb8:Root:  01bdc110(GCTest.A) ----//Still remains**
      ESP:20ebbc:Root:  01bdc110(GCTest.A)         
      Scan Thread 2404 OSTHread 964
    

    弱参照が削除されていないことを確認します。

       !GCRoot 01bdc210 //(address of weak reference)
       Scan Thread 2920 OSTHread b68
       **ESP:20e968:Root:  01bdc210(System.WeakReference) // Created by us** 
       ESP:20ebb4:Root:  01bdc210(System.WeakReference) // other in mscorlib
       ESP:20ebc0:Root:  01bdc210(System.WeakReference) // other in mscorlib
       Scan Thread 2404 OSTHread 964
    

2、初期化子なし、

    !GCRoot 01bdd4e0 
    Scan Thread 2920 OSTHread b68
    ESP:20e960:Root:  01bdd4e0(GCTest.A)
    ESP:20eba8:Root:  01bdd4e0(GCTest.A)
    ESP:20ebc4:Root:  01bdd4e0(GCTest.A)
    Scan Thread 2404 OSTHread 964

   **No extra stack reference.**
于 2013-01-24T12:32:42.380 に答える