1
class A
{
   public int m_a;
}

void fun(ref int a)
{
   ...
}

fun(ref new A().m_a);

楽しみながら、「ref int a」は、楽しみから戻る前にオブジェクト(new A())が再利用されないようにするにはどうすればよいですか?

<example 0>
using System;

class A
{
    public int m_a;
    ~A()
    {
        Console.WriteLine("~A()");
    }
}

class Program
{
    static void fun(ref int a)
    {
        Console.WriteLine("Begin<<<<<<<<<<<<<<");
        a++;
        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("End>>>>>>>>>>>>>>>>>");
    }

    static void Main(string[] args)
    {
        fun(ref new A().m_a);

        Console.WriteLine("Over");
    }
}


output:
Begin<<<<<<<<<<<<<<
~A()
End>>>>>>>>>>>>>>>>>
Over

<example 1>
using System;

class A
{
    public int m_a;
    ~A()
    {
        Console.WriteLine("~A()");
    }
}

class Program
{
    static void fun(ref int a)
    {
        Console.WriteLine("Begin<<<<<<<<<<<<<<");
        a++;
        GC.Collect();
        GC.WaitForPendingFinalizers();

        //add a code
        a++;

        Console.WriteLine("End>>>>>>>>>>>>>>>>>");
    }

    static void Main(string[] args)
    {
        fun(ref new A().m_a);

        Console.WriteLine("Over");
    }
}

output:
Begin<<<<<<<<<<<<<<
End>>>>>>>>>>>>>>>>>
Over
~A()

VSのリリースモードでビルドしてください。ASMコードを表示すると、2行だけが追加されます。

             a++;
0000002f  mov         eax,dword ptr [ebp-4] 
00000032  inc         dword ptr [eax] 

2つの例の間の他の部分は同じです。GCは、変数aがマシンコードで役に立たなくなったことをどのように確認しますか?

4

3 に答える 3

2

aでの使用方法によって異なりますfun。GCは、特定のオブジェクトがルート化されているかどうかを判別できます。この場合、はのインスタンスのaフィールドのエイリアスであり、オブジェクトはルート化されていると見なされます。ただし、JITコンパイラが、その時点以降のメソッドで使用されていないと判断した場合、のインスタンスはルート化されなくなり、ガベージコレクションの対象になります。m_aAafunA

いくつかの例:

void fun(ref int a)
{
   // forgot to use a. our object is already eligible for GC!
   for(int i = 0; i < 10; i++)
   {
      Console.WriteLine(i);
   }
}

void fun2(ref int a)
{       
   for(int i = 0; i < 10; i++)
   {
      Console.WriteLine(a);
   }
   // GC has to wait until a is no longer in use. now our object is eligible for GC.
}

void fun3(ref int a)
{
   // can't gc yet. a is still being used.
   int b = a;
   // b has a copy of the value in a so now our object is eligible for GC.
   for(int i = 0; i < 10; i++)
   {
      Console.WriteLine(b);
   }
}

アップデート:

私はこれがclrでどのように実装されるかについての専門家ではありませんが、refを使用すると、フィールドm_aへのマネージポインターがに渡されることを理解していfunます。GCが実行されると、静的なヒープオブジェクトへの参照、すべてのスレッドのコールスタック、およびレジスタからルートが決定されます。ここで推測していますが、フィールドへのマネージポインターにはm_a、コンテナーオブジェクトへの参照が格納されている可能性があります。または、GCが、特定の管理対象ポインターがどのオブジェクトにあるかを判別できる場合もあります。どちらの方法でも、オブジェクトはその管理対象参照からルート化されているとマークされます。

于 2012-07-03T07:33:05.467 に答える
1

この古いプレゼンテーション(スライド30以降)で十分だと思いましたが、コメント欄で少し前後に展開していたので、答えてみようと思いました。

JITがメソッドを準備するときはいつでも、メソッドの特定のポイントでどのローカル変数スロットが「ライブ」であるかをマップする「テーブル」も作成します。したがって、GCが各スレッドを検査するとき、GCはそのスレッドの命令ポインターを取得し、テーブルを参照し、それを使用して現在のメソッド内のライブ参照を決定します。

GCに何かを通知する必要がある特定のメソッドのマシンコードには何も書き込まれません。JIT分析はコード内のすべてのパスを対象とし、メソッドごとに1回だけ実行する必要があります。

デバッグでは、JITはすべての変数をメソッドの本体全体で使用されるものとしてマークします。これにより、参照は厳密に必要な期間よりも長く存続しますが、メソッドで最後に使用された後の変数の状態を調べることができます(たとえば、Localsまたは自動車ウィンドウ、または突然変数を参照したいその他の方法)

于 2012-07-03T13:09:23.153 に答える
1

事前にAインスタンスを作成し、次の行に沿ってそのインスタンスへの参照を保持する必要があります。

    A a = new A();
    fun(ref a.m_a);

それ以外の場合、funが戻ると、Aの新しいインスタンスはスコープ外になり、ガベージコレクションの対象になります。

于 2012-07-03T07:27:24.973 に答える