web-dev-qa-db-ja.com

いつ.Netで弱参照を使用するのですか?

個人的に.NetでWeakReference型を使用する必要があった状況に遭遇したことはありませんが、一般的には、キャッシュで使用する必要があると考えられています。 Jon Harrop博士は、キャッシュ内のWeakReferencesを answer から this の質問で使用することに対して非常に良い事例を示しました。

AS3開発者が弱い参照を使用してメモリフットプリントを節約することについて話していることもよく耳にしますが、会話に基づいて、意図した目標を必ずしも達成せずに複雑さを追加しているようで、ランタイムの動作はかなり予測不可能です。そのため、多くの人は単純にそれをあきらめ、代わりにメモリ使用量をより慎重に管理し、コードを最適化してメモリの使用量を減らします(または、CPUサイクルを増やしてメモリフットプリントを小さくする代わりに)。

ジョンハーロップ博士はまた、彼の回答の中で、.Net弱参照はソフトではなく、gen0には積極的な弱参照のコレクションがあると指摘しました。 [〜#〜] msdn [〜#〜] によると、長い弱参照はオブジェクトbut the state of the object remains unpredictable.!を再作成する可能性を与えます。

これらの特性を考えると、弱い参照が役立つ状況を考えることができません。おそらく誰かが私を啓発できるでしょうか?

57
theburningmonk

私が実際に私に実際に起こった次の3つの現実のシナリオで、弱参照の正当な実用的なアプリケーションを見つけました。

アプリケーション1:イベントハンドラー

あなたは起業家です。あなたの会社はWPFの スパークライン コントロールを販売しています。売上高は素晴らしいですが、サポート費用はあなたを殺しています。 spark行でいっぱいの画面をスクロールすると、CPUの占有率とメモリリークに不満を言う顧客が多すぎます。問題は、アプリが新しいsparkそれらは表示されますが、データバインディングが古いもののガベージコレクションの妨げになっています。

データバインディングだけでコントロールのガベージコレクションが妨げられないように、データバインディングとコントロールの間に弱い参照を導入します。次に、ファイナライザーをコントロールに追加して、収集時にデータバインディングを破棄します。

アプリケーション2:可変グラフ

あなたは次のジョン・カーマックです。 Tim Sweeneyのゲームを任天堂のWiiのように見せるために、階層的なサブディビジョンサーフェスの独創的な新しいグラフベースの表現を発明しました。明らかに私はあなたに言うつもりはありません 正確にそれがどのように機能するか しかし、それはすべて、この可変グラフの中心にあり、頂点の近傍はDictionary<Vertex, SortedSet<Vertex>>。プレーヤーが走り回るにつれて、グラフのトポロジーは変化し続けます。問題は1つだけです。データ構造が実行時に到達できないサブグラフを排出しているため、それらを削除する必要があるか、メモリをリークします。幸運にもあなたは天才なので、到達できないサブグラフを見つけて収集するために特別に設計されたアルゴリズムのクラスがあることを知っています:ガベージコレクター!あなたは 件に関するリチャードジョーンズの優れたモノグラフ を読みますが、それはあなたを当惑させ、差し迫った締め切りを心配させます。職業はなんですか?

Dictionaryを弱いハッシュテーブルに置き換えるだけで、既存のGCを便乗させて、到達不能なサブグラフを自動的に収集させることができます。フェラーリの広告をめくって戻ることに戻る。

アプリケーション3:木を飾る

あなたはキーボードで円柱形の部屋の天井からぶら下がっています。誰かがあなたを見つけるまでに60秒間、大きなデータをふるいにかけます。 GCを使用してASTのフラグメントが分析された後、それらを収集します。ただし、各AST Nodeそしてあなたはそれを速く必要とします。

Dictionary<Node, Metadata>メタデータを各ノードに関連付けますが、クリアしない限り、ディクショナリから古いASTノードへの強い参照は、それらを存続させ、メモリをリークします。解決策は、弱いハッシュテーブルは、キーへの弱い参照のみを保持し、キーが到達不能になったときにガベージがキーと値のバインディングを収集します。次に、AST =ノードは到達不能になり、ガベージコレクションされ、キーと値のバインディングがディクショナリから削除されて、対応するメタデータに到達できないため、ノードも収集されます。その後、メインループが終了した後に行う必要があるのは、エアベントを通って上方にスライドすることだけです。警備員が入ってきたときと同じように交換することを忘れないでください.

実際に私に起こったこれら3つの実際のアプリケーションすべてにおいて、私はGCを可能な限り積極的に収集したいことに注意してください。これが正当なアプリケーションである理由です。他の誰もが間違っています。

40
Jon Harrop

これらの特性を考えると、弱い参照が役立つ状況を考えることができません。おそらく誰かが私を啓発できるでしょうか?

Microsoft文書 弱いイベントパターン

アプリケーションでは、イベントソースに接続されているハンドラーが、ハンドラーをソースに接続しているリスナーオブジェクトと連携して破棄されない可能性があります。この状況は、メモリリークを引き起こす可能性があります。 Windows Presentation Foundation(WPF)は、特定のイベント専用のマネージャークラスを提供し、そのイベントのリスナーにインターフェイスを実装することにより、この問題に対処するために使用できるデザインパターンを導入しています。この設計パターンは、弱いイベントパターンと呼ばれます。

...

弱いイベントパターンは、このメモリリークの問題を解決するように設計されています。弱いイベントパターンは、リスナーがイベントに登録する必要があるときにいつでも使用できますが、リスナーは登録を解除するタイミングを明示的に知りません。弱いイベントパターンは、ソースのオブジェクトライフタイムがリスナーの有効なオブジェクトライフタイムを超える場合にも使用できます。 (この場合、有用かどうかはユーザーが決定します。)弱いイベントパターンを使用すると、リスナーは、リスナーのオブジェクト有効期間の特性に影響を与えることなく、イベントを登録および受信できます。実際には、ソースからの暗黙の参照は、リスナーがガベージコレクションの対象かどうかを決定しません。 参照は弱い参照であるため、弱いイベントパターンと関連するAPIの名前。リスナーはガベージコレクションまたはその他の方法で破棄できます。ソースは、破棄されたオブジェクトへの収集不可能なハンドラー参照を保持せずに続行できます。

19
ta.speot.is

これを最初に出して、戻ってきます。

WeakReferenceは、オブジェクトのタブを保持したいが、観察によってそのオブジェクトが収集されないようにしたくない場合に便利です。

最初から始めましょう:

-意図しない違反については事前に謝罪しますが、視聴者に伝えることができないため、少しの間、「ディックアンドジェーン」レベルに戻ります。

したがって、オブジェクトXを取得したら、それを_class Foo_のインスタンスとして指定してみましょう。 「人は島ではない」と同じように、オブジェクトがIslandhoodに昇格できる方法はわずかですが、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、もちろんこれは真実ではありません)

  • オブジェクトのコレクションに何かを「ブロードキャスト」したいが、それらを存続させることになりたくない。 _List<WeakReference>_は簡単に維持でき、場所__ref.Target == null_を削除することで整理することもできます

13
JerKimball

enter image description here

ユーザーがソフトウェアを長時間実行するとメモリがどんどん増え、再起動するまでに時間がかかることに気づきがちですが、追跡が本当に難しい論理リークのように?私はしません。

ユーザーが上記のアプリケーションリソースの削除をリクエストしたときに、Thing2は以下のようなイベントを適切に処理できません:

  1. ポインタ
  2. 強い参照
  3. 弱い参照

...そして、テスト中にこれらのミスの1つが検出される可能性が高く、そのようなミスは検出されず、ステルスファイターバグのようにレーダーの下を飛行します。所有権の共有は、ほとんどの場合、無意味なアイデアです。

4
user204677

効果的に使用される弱い参照の非常に例証的な例は ConditionalWeakTable であり、これはDLRが(他の場所の中で)オブジェクトに追加の「メンバー」をアタッチするために使用します。

テーブルにオブジェクトを存続させたくない。この概念は、弱い参照がなければ機能しません。

しかし、弱参照がバージョン1.1以降.NETの一部となっているため、弱参照の使用はすべて、言語に追加されてからかなり時間が経ったように思えます。それはあなたが追加したいもののように思えるので、確定的な破壊の欠如は、言語機能に関する限り、あなたを追い詰めません。

1
GregRos