これを最初に出して戻ってみましょう。
WeakReferenceは、オブジェクトのタブを保持したいが、そのオブジェクトが収集されないように観察したくない場合に便利です。
それでは最初から始めましょう:
-意図しない攻撃については事前におapび申し上げますが、しばらくの間は「ディックとジェーン」レベルに戻ります。
そのため、オブジェクトを取得したらX
-インスタンスとして指定しましょうclass Foo
-それ自身で生きることはできません(ほとんど真)。「人は島ではありません」と同じように、オブジェクトを島に昇格させる方法はいくつかありますが、CLRのGCルートと呼ばれています。GCルートであること、またはGCルートへの接続/参照の確立されたチェーンを持つことは、基本的Foo x = new Foo()
にガベージコレクションを取得するかどうかを決定するものです。
ヒープまたはスタックウォーキングによってGCルートに戻ることができない場合、事実上孤立しており、次のサイクルでマーク/収集される可能性があります。
この時点で、いくつかの恐ろしい仕掛けの例を見てみましょう。
まず、私たちのFoo
:
public class Foo
{
private static volatile int _ref = 0;
public event EventHandler FooEvent;
public Foo()
{
_ref++;
Console.WriteLine("I am #{0}", _ref);
}
~Foo()
{
Console.WriteLine("#{0} dying!", _ref--);
}
}
かなり簡単です-スレッドセーフではないので、試してはいけませんが、アクティブなインスタンスの大まかな「参照カウント」と、それらがファイナライズされたときに減少します。
では、を見てみましょうFooConsumer
:
public class NastySingleton
{
// Static member status is one way to "get promoted" to a GC root...
private static NastySingleton _instance = new NastySingleton();
public static NastySingleton Instance { get { return _instance;} }
// testing out "Hard references"
private Dictionary<Foo, int> _counter = new Dictionary<Foo,int>();
// testing out "Weak references"
private Dictionary<WeakReference, int> _weakCounter = new Dictionary<WeakReference,int>();
// Creates a strong link to Foo instance
public void ListenToThisFoo(Foo foo)
{
_counter[foo] = 0;
foo.FooEvent += (o, e) => _counter[foo]++;
}
// Creates a weak link to Foo instance
public void ListenToThisFooWeakly(Foo foo)
{
WeakReference fooRef = new WeakReference(foo);
_weakCounter[fooRef] = 0;
foo.FooEvent += (o, e) => _weakCounter[fooRef]++;
}
private void HandleEvent(object sender, EventArgs args, Foo originalfoo)
{
Console.WriteLine("Derp");
}
}
したがって、既にGCルートであるオブジェクト(具体的には、このアプリケーションを実行しているアプリドメインに直接チェーンを介してルートされますが、それは別のトピックです)には2つのメソッドがありますFoo
インスタンスにラッチする-テストしてみましょう:
// Our foo
var f = new Foo();
// Create a "hard reference"
NastySingleton.Instance.ListenToThisFoo(f);
// Ok, we're done with this foo
f = null;
// Force collection of all orphaned objects
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
さて、上記から、一度参照されたオブジェクトf
が「収集可能」になると期待しますか?
いいえ、現在その参照を保持している別のオブジェクトがあるためです- Dictionary
そのSingleton
静的インスタンス内。
わかりました、弱いアプローチを試みましょう:
f = new Foo();
NastySingleton.Instance.ListenToThisFooWeakly(f);
// Ok, we're done with this foo
f = null;
// Force collection of all orphaned objects
// This should collect # 2 - you'll see a "#2 dying"
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
さて、--- Foo
that-was-once- への参照を強打するf
と、オブジェクトへの「ハード」参照はなくなるため、収集可能になります- WeakReference
弱いリスナーによって作成されたものはそれを防ぎません。
良い使用例:
イベントハンドラー(最初にこれを読んでください:C#の弱いイベント)
「再帰的参照」を引き起こす状況があります(つまり、オブジェクトAはオブジェクトBを参照し、オブジェクトBはオブジェクトAを参照し、「メモリリーク」とも呼ばれます)(編集:derp、もちろんこれはありません真実ではない)
オブジェクトのコレクションに何かを「ブロードキャスト」したいのですが、オブジェクトを生きたままにしたくないのです。a List<WeakReference>
は簡単に維持でき、さらにどこを削除することで整理することもできます。ref.Target == null