3

C# でアクションを使用していますが、GC でオブジェクトを適切に収集したい場合、アクションのインスタンスを null に設定する必要があるかどうか疑問に思っていました。次に例を示します。

public class A
{
 public Action a;
}

public class B
{
  public string str;
}

public class C
{
 public void DoSomething()
 {
   A aClass = new A();
   B bClass = new B();
   aClass.a = () => { bClass.str = "Hello"; }
 }
}

Main メソッド内には、次のようなものがあります。

public void Main(...)
{
  C cClass = new C();
  cClass.DoSomething();

  Console.WriteLine("At this point I dont need object A or B anymore so I would like the GC to collect them automatically.");
  Console.WriteLine("Therefore I am giving GC time by letting my app sleep");
  Thread.Sleep(3000000);
  Console.WriteLine("The app was propably sleeping long enough for GC to have tried collecting objects at least once but I am not sure if A and B objects have really been collected");
 }
}

Console.WriteLine のテキストを読んでください。ここで私が求めていることを理解するのに役立ちます。

GC に関する私の理解をこの例に当てはめると、A は B のインスタンスを保持しているため破棄できないため、GC はオブジェクトを収集しません。

これらの 2 つのオブジェクトを適切に収集するにはどうすればよいですか? アプリケーションの終了前にGCがオブジェクトを収集できるようにするためだけに、アクションのインスタンスをnullに設定する必要がありますか?それとも、AやBなどのアクションを持つオブジェクトを破棄する方法を知っているGCによる非常にスマートなメカニズムがすでにありますか?

編集:問題は、GC とオブジェクトの適切な収集に関するものです。メソッド collect() を呼び出すことではありません。

4

2 に答える 2

19

この質問には多くの問題があります。あなたの質問に直接答えるのではなく、あなたが尋ねるべき質問に答えるつもりです。

最初に、GC についてのあなたの考えを誤解させましょう。

長時間スリープするとガベージコレクタが起動しますか?

いいえ。

ガベージ コレクタをアクティブにするものは何ですか?

GC.Collect()テスト目的で、とを使用できますGC.WaitForPendingFinalizers()。これらはテスト目的でのみ使用してください。非常にまれな状況を除いて、本番コードでそれらを使用することは悪い習慣です。

通常の状況では、GC をトリガーするものは複雑です。GC は高度に調整された機械です。

閉じた外部変数に関する限り、ガベージ コレクションのセマンティクスは何ですか?

デリゲートに変換されるラムダの閉じた外部変数の有効期間は、デリゲートの有効期間よりも短くならないように延長されます。

Action参照型の外部ローカル変数で閉じられたラムダで初期化される型の変数があるとします。その変数によって参照されるオブジェクトをコレクションの対象にするには、型の変数を に設定する必要がありますActionnull?

ほとんどの場合、いいえ。ガベージ コレクタは非常にスマートです。心配する必要はありません。最終的に、ランタイムはAction、ライブ ルートが変数に到達できないと判断し、コレクションの対象にします。閉鎖された外部変数は適格になります。

より早い方への参照を破棄したい状況が非常にまれにあるかもしれませんが、そのような状況はめったにありませんAction。ほとんどの場合、干渉することなく GC に任せてください。

外部変数の寿命が長くなりすぎる状況はありますか?

はい。検討:

void M()
{
    Expensive e = new Expensive();
    Cheap c = new Cheap();
    Q.longLived = ()=>c; // static field
    Q.shortLived = ()=>e; // static field
}

M()実行されると、両方のデリゲートに対してクロージャーが作成されます。shortLivedがすぐnullに設定され、遠い将来にlongLived設定されるとします。null残念ながら、両方のローカル変数の有効期間は、 によって参照されるオブジェクトの有効期間まで延長されますがlongLived、 だけcはまだ到達可能です。高価なリソースeは、参照がなくなるまで解放されませんlongLived

多くのプログラミング言語がこの問題を抱えています。JavaScript、Visual Basic、および C# の一部の実装には、すべてこの問題があります。C#/VB の Roslyn リリースで修正するという話もありますが、それが実現するかどうかはわかりません。

その場合の解決策は、そもそもその状況を回避することです。デリゲートの 1 つが他のデリゲートよりもはるかに長く存続する場合は、クロージャを共有する 2 つのラムダを作成しないでください。

閉じた外部変数ではないローカルがコレクションの対象になるのはどのような状況ですか?

ランタイムがローカルから再度読み取ることができないことを証明できる瞬間、それが参照するものはコレクションの対象になります (もちろん、ローカルが唯一のルートであると仮定します)。サンプル プログラムでは、参照が含まれている必要はありません。メソッドの最後まで有効です。実際、あるスレッドのオブジェクトが別のスレッドのコンストラクター内にあるときに、GC がそのオブジェクトの割り当てを解除している可能性がある状況がまれにあります。GC は、何が死んでいるかを非常に積極的に判断する可能性があるため、注意してください。aClassbClass

アグレッシブな GC に直面して何かを存続させるにはどうすればよいですか?

GC.KeepAlive()もちろん。

于 2013-05-14T19:45:42.773 に答える
6

C# でアクションを使用していますが、GC でオブジェクトを適切に収集したい場合、アクションのインスタンスを null に設定する必要があるかどうか疑問に思っていました。

必ずしも。デリゲートを参照するオブジェクトに到達できない限り、デリゲートは GC の対象となります。

そうは言っても、あなたの例でaClassは、bClassまだ有効な変数であり、到達可能なオブジェクトを参照しています。これは、aClass.aまだ到達可能であり、GC の対象ではないため、収集されないことを意味します。

これらをガベージ コレクションする場合は、オブジェクト参照 ( aClass) を明示的に null に設定して、Aインスタンスとそれに含まれるデリゲートが到達可能なオブジェクトではなくなるようにする必要がありますGC.Collect。コード内で GC がトリガーされることはありません。

于 2013-05-14T19:19:06.930 に答える