System.WeakReferenceの実践的な使い方
-
08-06-2019 - |
質問
わかりました System.WeakReference しかし、それが何に役立つのかという実際的な例が私には理解できないようです。私には、クラス自体がハックのように思えます。私が見た例では WeakReference が使用されている問題を解決する、より良い手段が他にあるように思えます。実際に WeakReference を使用する必要がある標準的な例は何ですか?私たちは手に入れようとしているのではありませんか もっと遠くへ このタイプの動作とこのクラスの使用を避けますか?
解決
役に立つ例の 1 つは、DB4O オブジェクト指向データベースを実行している人たちです。そこでは、WeakReferences が一種のライト キャッシュとして使用されます。アプリケーションが実行している間だけオブジェクトをメモリ内に保持し、実際のキャッシュを最上位に置くことができます。
別の用途は、弱いイベント ハンドラーの実装です。現在、.NET アプリケーションにおけるメモリ リークの大きな原因の 1 つは、イベント ハンドラーの削除を忘れていることです。例えば。
public MyForm()
{
MyApplication.Foo += someHandler;
}
問題がわかりましたか?上記のスニペットでは、MyApplication がメモリ内で生きている限り、MyForm はメモリ内で永久に生き続けます。10 個の MyForms を作成し、それらをすべて閉じると、10 個の MyForms はメモリ内に残り、イベント ハンドラーによって維持されます。
「WeakReference」を入力します。WeakReferences を使用して弱いイベント ハンドラーを構築すると、someHandler が MyApplication.Foo に対する弱いイベント ハンドラーになり、メモリ リークが修正されます。
これは単なる理論ではありません。DidItWith.NET ブログの Dustin Campbell が投稿 弱いイベント ハンドラーの実装 System.WeakReference を使用します。
他のヒント
これを使用して、未使用のエントリが自動的にガベージ コレクションされるキャッシュを実装します。
class Cache<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
{ Dictionary<TKey,WeakReference> dict = new Dictionary<TKey,WeakReference>();
public TValue this[TKey key]
{ get {lock(dict){ return getInternal(key);}}
set {lock(dict){ setInteral(key,value);}}
}
void setInteral(TKey key, TValue val)
{ if (dict.ContainsKey(key)) dict[key].Target = val;
else dict.Add(key,new WeakReference(val));
}
public void Clear() { dict.Clear(); }
/// <summary>Removes any dead weak references</summary>
/// <returns>The number of cleaned-up weak references</returns>
public int CleanUp()
{ List<TKey> toRemove = new List<TKey>(dict.Count);
foreach(KeyValuePair<TKey,WeakReference> kv in dict)
{ if (!kv.Value.IsAlive) toRemove.Add(kv.Key);
}
foreach (TKey k in toRemove) dict.Remove(k);
return toRemove.Count;
}
public bool Contains(string key)
{ lock (dict) { return containsInternal(key); }
}
bool containsInternal(TKey key)
{ return (dict.ContainsKey(key) && dict[key].IsAlive);
}
public bool Exists(Predicate<TValue> match)
{ if (match==null) throw new ArgumentNullException("match");
lock (dict)
{ foreach (WeakReference weakref in dict.Values)
{ if ( weakref.IsAlive
&& match((TValue) weakref.Target)) return true;
}
}
return false;
}
/* ... */
}
私はミックスインで状態を保持するために弱い参照を使用します。ミックスインは静的であるため、静的オブジェクトを使用して状態を非静的オブジェクトにアタッチする場合、どれくらいの時間がかかるかわからないことに注意してください。したがって、 Dictionary<myobject, myvalue>
私は Dictionary<WeakReference,myvalue>
ミックスインが物事を長時間引きずらないようにするため。
唯一の問題は、アクセスするたびに、無効な参照もチェックして削除することです。もちろん、何千人もいない限り、誰かを傷つけるわけではありません。
使用する理由は 2 つあります WeakReference
.
静的として宣言されたグローバル オブジェクトの代わりに:グローバル オブジェクトは静的フィールドとして宣言され、静的フィールドは、
AppDomain
GC 化されます。したがって、メモリ不足例外が発生する危険があります。代わりに、グローバル オブジェクトをWeakReference
. 。たとえWeakReference
それ自体が静的と宣言されている場合、メモリが不足している場合、それが指すオブジェクトは GC 処理されます。基本的には、使用します
wrStaticObject
の代わりにstaticObject
.class ThingsWrapper { //private static object staticObject = new object(); private static WeakReference wrStaticObject = new WeakReference(new object()); }
AppDomain の場合に静的オブジェクトがガベージ コレクションされることを証明するシンプルなアプリ。
class StaticGarbageTest { public static void Main1() { var s = new ThingsWrapper(); s = null; GC.Collect(); GC.WaitForPendingFinalizers(); } } class ThingsWrapper { private static Thing staticThing = new Thing("staticThing"); private Thing privateThing = new Thing("privateThing"); ~ThingsWrapper() { Console.WriteLine("~ThingsWrapper"); } } class Thing { protected string name; public Thing(string name) { this.name = name; Console.WriteLine("Thing() " + name); } public override string ToString() { return name; } ~Thing() { Console.WriteLine("~Thing() " + name); } }
以下の出力からのメモ
staticThing
後でも最後に GC されますThingsWrapper
は - つまりいつGCされたかAppDomain
GC 化されます。Thing() staticThing Thing() privateThing ~Thing() privateThing ~ThingsWrapper ~Thing() staticThing
代わりにラップすることができます
Thing
でWeakReference
. 。としてwrStaticThing
GC できる場合は、遅延読み込みメソッドが必要になりますが、簡潔にするために省略しました。class WeakReferenceTest { public static void Main1() { var s = new WeakReferenceThing(); s = null; GC.Collect(); GC.WaitForPendingFinalizers(); if (WeakReferenceThing.wrStaticThing.IsAlive) Console.WriteLine("WeakReference: {0}", (Thing)WeakReferenceThing.wrStaticThing.Target); else Console.WriteLine("WeakReference is dead."); } } class WeakReferenceThing { public static WeakReference wrStaticThing; static WeakReferenceThing() { wrStaticThing = new WeakReference(new Thing("wrStaticThing")); } ~WeakReferenceThing() { Console.WriteLine("~WeakReferenceThing"); } //lazy-loaded method to new Thing }
以下の出力に注意してください
wrStaticThing
GC スレッドが呼び出されたときに GC が実行されます。Thing() wrStaticThing ~Thing() wrStaticThing ~WeakReferenceThing WeakReference is dead.
初期化に時間がかかるオブジェクトの場合:初期化に時間がかかるオブジェクトを GC 処理する必要はありません。それを避けるために静的参照を保持するか(上記の点からの欠点があります)、または使用することができます
WeakReference
.