Praktische Verwendung von System.WeakReference
-
08-06-2019 - |
Frage
Ich verstehe was System.WeakReference tut, aber was ich anscheinend nicht begreifen kann, ist ein praktisches Beispiel dafür, wofür es nützlich sein könnte.Die Klasse selbst scheint mir, nun ja, ein Hack zu sein.Mir scheint, dass es andere, bessere Möglichkeiten gibt, ein Problem zu lösen, bei dem in den Beispielen, die ich gesehen habe, eine WeakReference verwendet wird.Was ist das kanonische Beispiel dafür, wo Sie wirklich eine WeakReference verwenden müssen?Versuchen wir nicht, es zu bekommen? weiter weg von dieser Art von Verhalten und Nutzung dieser Klasse?
Lösung
Ein nützliches Beispiel sind die Leute, die die objektorientierte DB4O-Datenbank betreiben.Dort werden WeakReferences als eine Art Light-Cache verwendet:Dadurch bleiben Ihre Objekte nur so lange im Speicher wie Ihre Anwendung, sodass Sie einen echten Cache darüber legen können.
Eine andere Verwendung wäre die Implementierung schwacher Event-Handler.Eine der Hauptursachen für Speicherlecks in .NET-Anwendungen ist derzeit das Vergessen, Ereignishandler zu entfernen.Z.B.
public MyForm()
{
MyApplication.Foo += someHandler;
}
Sehen Sie das Problem?Im obigen Snippet bleibt MyForm für immer im Speicher erhalten, solange MyApplication im Speicher aktiv ist.Erstellen Sie 10 MyForms, schließen Sie sie alle. Ihre 10 MyForms bleiben weiterhin im Speicher und werden vom Ereignishandler am Leben gehalten.
Geben Sie WeakReference ein.Sie können mit WeakReferences einen schwachen Event-Handler erstellen, sodass someHandler ein schwacher Event-Handler für MyApplication.Foo ist, und so Ihre Speicherlecks beheben!
Das ist nicht nur Theorie.Dustin Campbell vom DidItWith.NET-Blog gepostet eine Implementierung schwacher Event-Handler mit System.WeakReference.
Andere Tipps
Ich verwende es, um einen Cache zu implementieren, in dem nicht verwendete Einträge automatisch durch Müll gesammelt werden:
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;
}
/* ... */
}
Ich verwende schwache Referenzen für die Zustandshaltung in Mixins.Denken Sie daran, dass Mixins statisch sind. Wenn Sie also ein statisches Objekt verwenden, um den Status an ein nicht statisches Objekt anzuhängen, wissen Sie nie, wie lange es dauern wird.Anstatt also eine zu behalten Dictionary<myobject, myvalue>
Ich behalte eine Dictionary<WeakReference,myvalue>
um zu verhindern, dass das Mixin die Dinge zu lange in die Länge zieht.
Das einzige Problem ist, dass ich bei jedem Zugriff auch nach toten Referenzen suche und diese entferne.Nicht, dass sie irgendjemandem schaden würden, es sei denn, es wären Tausende.
Es gibt zwei Gründe, warum Sie es verwenden würden WeakReference
.
Anstelle globaler Objekte, die als statisch deklariert sind:Globale Objekte werden als statische Felder deklariert und statische Felder können bis zum GC nicht durch Müll gesammelt werden
AppDomain
ist GC'ed.Sie riskieren also Ausnahmen wegen unzureichendem Arbeitsspeicher.Stattdessen können wir das globale Objekt in a einschließenWeakReference
.Obwohl dieWeakReference
selbst als statisch deklariert ist, wird das Objekt, auf das es zeigt, einer GC unterzogen, wenn der Speicher knapp wird.Grundsätzlich verwenden
wrStaticObject
anstattstaticObject
.class ThingsWrapper { //private static object staticObject = new object(); private static WeakReference wrStaticObject = new WeakReference(new object()); }
Einfache App, um zu beweisen, dass ein statisches Objekt zusammen mit der AppDomain im Garbage Collection-Speicher erfasst wird.
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); } }
Hinweis aus der Ausgabe unten
staticThing
wird auch danach ganz am Ende GC'edThingsWrapper
ist - d.h.GC'ed wannAppDomain
ist GC'ed.Thing() staticThing Thing() privateThing ~Thing() privateThing ~ThingsWrapper ~Thing() staticThing
Stattdessen können wir einpacken
Thing
in einemWeakReference
.AlswrStaticThing
GC-fähig sein kann, benötigen wir eine Lazy-Loaded-Methode, die ich der Kürze halber weggelassen habe.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 }
Beachten Sie die Ausgabe darunter
wrStaticThing
wird GC-gesteuert, wenn der GC-Thread aufgerufen wird.Thing() wrStaticThing ~Thing() wrStaticThing ~WeakReferenceThing WeakReference is dead.
Für Objekte, deren Initialisierung zeitaufwändig ist:Sie möchten nicht, dass Objekte, deren Initialisierung zeitaufwändig ist, einer GC unterzogen werden.Sie können entweder eine statische Referenz beibehalten, um dies zu vermeiden (mit den oben genannten Nachteilen), oder sie verwenden
WeakReference
.