Ich persönlich bin nicht auf eine Situation gestoßen, in der ich den Typ WeakReference in .Net verwenden musste, aber die weit verbreitete Überzeugung scheint zu sein, dass er in Caches verwendet werden sollte. In seiner Antwort auf diese Frage sprach sich Dr. Jon Harrop sehr gut gegen die Verwendung von WeakReferences in Caches aus .
Ich habe auch oft gehört, wie AS3-Entwickler davon gesprochen haben, schwache Referenzen zu verwenden, um Speicherplatz zu sparen, aber aufgrund der Gespräche, die ich geführt habe, scheint dies die Komplexität zu erhöhen, ohne das beabsichtigte Ziel zu erreichen, und das Laufzeitverhalten ist ziemlich unvorhersehbar. So sehr, dass viele einfach aufgeben und stattdessen die Speichernutzung sorgfältiger verwalten / ihren Code so optimieren, dass er weniger speicherintensiv ist (oder den Kompromiss aus mehr CPU-Zyklen und geringerem Speicherbedarf eingeht).
Dr. Jon Harrop wies in seiner Antwort auch darauf hin, dass die .Net-Schwachstellen nicht weich sind und es bei gen0 eine aggressive Sammlung von Schwachstellen gibt. Laut MSDN , langer schwacher Verweise gibt Ihnen die Möglichkeit , ein Objekt zu erstellen, but the state of the object remains unpredictable.
!
Angesichts dieser Eigenschaften kann ich mir keine Situation vorstellen, in der schwache Referenzen nützlich wären, vielleicht könnte mich jemand aufklären?
quelle
Antworten:
Ich habe in den folgenden drei realen Szenarien, die mir persönlich tatsächlich passiert sind, legitime praktische Anwendungen von schwachen Referenzen gefunden:
Anwendung 1: Eventhandler
Du bist ein Unternehmer. Ihr Unternehmen vertreibt eine Funkenstreckensteuerung für WPF. Der Verkauf ist großartig, aber die Supportkosten bringen Sie um. Zu viele Kunden beklagen sich über CPU-Überlastung und Speicherverluste, wenn sie durch mit Funkenlinien gefüllte Bildschirme scrollen. Das Problem ist, dass ihre App neue Funkenlinien erstellt, wenn sie angezeigt werden, aber die Datenbindung verhindert, dass die alten mit Müll eingesammelt werden. Wie geht's?
Fügen Sie einen schwachen Verweis zwischen der Datenbindung und Ihrem Steuerelement ein, damit die Datenbindung allein nicht mehr verhindert, dass Ihr Steuerelement durch Müll gesammelt wird. Fügen Sie Ihrem Steuerelement dann einen Finalizer hinzu, der die Datenbindung aufhebt, wenn sie erfasst wird.
Anwendung 2: Veränderbare Graphen
Du bist der nächste John Carmack. Sie haben eine geniale neue grafische Darstellung hierarchischer Unterteilungsoberflächen erfunden, mit der Tim Sweeneys Spiele wie eine Nintendo Wii aussehen. Natürlich werde ich Ihnen nicht genau sagen , wie es funktioniert, aber es dreht sich alles um diesen veränderlichen Graphen, in dem sich die Nachbarn eines Scheitelpunkts in a befinden
Dictionary<Vertex, SortedSet<Vertex>>
. Die Topologie des Diagramms ändert sich ständig, während der Player herumläuft. Es gibt nur ein Problem: Ihre Datenstruktur wirft während der Ausführung nicht erreichbare Untergraphen ab und Sie müssen sie entfernen, da sonst Speicherplatz verloren geht. Glücklicherweise sind Sie ein Genie und wissen, dass es eine Klasse von Algorithmen gibt, die speziell dafür entwickelt wurden, unerreichbare Untergraphen zu lokalisieren und zu sammeln: Müllsammler! Sie haben die ausgezeichnete Monographie von Richard Jones zu diesem Thema gelesenAber Sie sind verwirrt und besorgt über Ihre bevorstehende Frist. Wie geht's?Indem Sie einfach Ihre
Dictionary
durch eine schwache Hash-Tabelle ersetzen , können Sie den vorhandenen GC huckepack nehmen und Ihre nicht erreichbaren Subgraphen automatisch für Sie sammeln lassen! Zurück zu Ferrari-Anzeigen.Anwendung 3: Bäume schmücken
Sie hängen an der Decke eines zyklindrischen Raumes an einer Tastatur. Sie haben 60 Sekunden Zeit, um einige GROSSE DATEN zu sichten, bevor Sie jemand findet. Sie haben einen schönen Parser auf Stream-Basis erhalten, der sich auf die GC stützt, um AST-Fragmente zu sammeln, nachdem sie analysiert wurden. Sie stellen jedoch fest, dass Sie zusätzliche Metadaten für jeden AST
Node
benötigen und dass Sie diese schnell benötigen. Wie geht's?Sie könnten a verwenden
Dictionary<Node, Metadata>
, um jedem Knoten Metadaten zuzuordnen, aber die starken Verweise aus dem Wörterbuch auf alte AST-Knoten halten sie am Leben und führen zu Speicherverlusten. Die Lösung ist eine schwache Hash-Tabelle , die nur schwache Verweise auf Schlüssel enthält, und Garbage sammelt Schlüsselwertbindungen, wenn der Schlüssel nicht mehr erreichbar ist. Wenn AST-Knoten nicht mehr erreichbar sind, werden sie nicht mehr erfasst, und ihre Schlüsselwertbindung wird aus dem Wörterbuch entfernt. Die entsprechenden Metadaten sind dann nicht mehr erreichbar, sodass sie ebenfalls erfasst werden. Alles, was Sie nach Beendigung der Hauptschleife tun müssen, ist, durch die Lüftungsöffnung nach oben zu gleiten. Denken Sie daran, diese zu ersetzen, sobald der Wachmann hereinkommt.Beachten Sie, dass ich in allen drei dieser realen Anwendungen, die mir tatsächlich passiert sind, wollte, dass der GC so aggressiv wie möglich sammelt. Das ist der Grund, warum dies legitime Anträge sind. Alle anderen liegen falsch.
quelle
ConditionalWeakTable
in .NET).ConditionalWeakTable
Anwendungen 2 und 3 sind, während einige Personen in anderen Posts tatsächlich diese verwendenDictionary<WeakReference, T>
. Keine Ahnung warum - Sie würden immer eine Tonne NullenWeakReference
mit Werten erhalten, auf die von keinem Schlüssel zugegriffen werden kann, unabhängig davon, wie Sie dies tun. Ridik.Microsoft-Dokument Schwache Ereignismuster .
quelle
Lassen Sie mich dies zuerst ausdrücken und darauf zurückkommen:
Fangen wir also von vorne an:
Ich entschuldige mich im Voraus für jede unbeabsichtigte Beleidigung, aber ich werde für einen Moment wieder auf "Dick and Jane" -Niveau aufsteigen, da man es seinem Publikum niemals sagen kann.
Wenn Sie also ein Objekt haben
X
- geben wir es als Instanz von anclass Foo
-, kann es NICHT alleine leben (meistens wahr). Genauso wie "Kein Mensch ist eine Insel" gibt es nur wenige Möglichkeiten, wie ein Objekt zur Insel heraufgestuft werden kann - obwohl es in der CLR als GC-Root bezeichnet wird. Als GC-Root oder mit einer festgelegten Kette von Verbindungen / Verweisen auf eine GC-Root wird im Grunde genommen bestimmt, obFoo x = new Foo()
Müll gesammelt wird oder nicht .Wenn Sie nicht durch Haufen- oder Stapelgehen zu einer GC-Wurzel zurückkehren können, sind Sie effektiv verwaist und werden wahrscheinlich im nächsten Zyklus markiert / gesammelt.
Schauen wir uns an dieser Stelle einige schrecklich erfundene Beispiele an:
Erstens unser
Foo
:Ziemlich einfach - es ist nicht threadsicher. Versuchen Sie es also nicht, sondern führen Sie eine grobe "Referenzzählung" der aktiven Instanzen und Dekremente durch, wenn sie abgeschlossen sind.
Schauen wir uns nun Folgendes an
FooConsumer
:Wir haben also ein Objekt, das bereits ein eigenes GC-Stammverzeichnis hat (genauer gesagt, es wird über eine Kette direkt zu der App-Domäne gerootet, in der diese Anwendung ausgeführt wird, aber das ist ein anderes Thema), das über zwei Methoden verfügt Sich an eine
Foo
Instanz zu klinken - lass es uns ausprobieren:Würden Sie nun von oben erwarten, dass das Objekt, auf das einmal Bezug genommen wurde
f
, "sammelbar" ist?Nein, denn es gibt ein anderes Objekt, das jetzt einen Verweis darauf enthält - das
Dictionary
in dieserSingleton
statischen Instanz.Ok, lass uns den schwachen Ansatz ausprobieren:
Wenn wir nun unseren Verweis auf
Foo
das, was einmal war, verwechselnf
, gibt es keine "harten" Verweise mehr auf das Objekt, so dass es sammelbar ist - dasWeakReference
, was der schwache Zuhörer erschafft, wird das nicht verhindern.Gute Anwendungsfälle:
Ereignishandler (obwohl zuerst gelesen: Schwache Ereignisse in C # )
Sie haben eine Situation, in der Sie eine "rekursive Referenz" verursachen würden (dh Objekt A bezieht sich auf Objekt B, das sich auf Objekt A bezieht, auch als "Memory Leak" bezeichnet)(edit: derp, natürlich ist dies nicht der Fall) nicht wahr)Sie möchten etwas an eine Sammlung von Objekten "senden", aber Sie möchten nicht die Sache sein, die sie am Leben erhält. a
List<WeakReference>
kann leicht gewartet und sogar entfernt werdenref.Target == null
quelle
Wie logische Lecks, die wirklich schwer aufzuspüren sind, während Benutzer nur bemerken, dass das Ausführen Ihrer Software über einen längeren Zeitraum immer mehr Speicher beansprucht und immer langsamer wird, bis sie neu gestartet werden? Ich nicht.
Überlegen Sie, was passiert, wenn der Benutzer, wenn er das Entfernen der oben genannten Anwendungsressource anfordert,
Thing2
ein solches Ereignis unter folgenden Bedingungen nicht ordnungsgemäß behandelt:... und unter welchen Umständen ein solcher Fehler wahrscheinlich beim Testen aufgefallen wäre und welcher nicht und wie ein Stealth-Fighter-Käfer unter dem Radar fliegen würde. Geteilte Eigentümerschaft ist häufig eine unsinnige Idee.
quelle
Ein sehr anschauliches Beispiel für nützliche schwache Referenzen ist die ConditionalWeakTable , mit der das DLR (unter anderem) zusätzliche "Mitglieder" an Objekte anfügt.
Sie möchten nicht, dass der Tisch das Objekt am Leben erhält. Dieses Konzept könnte ohne schwache Referenzen einfach nicht funktionieren.
Mir scheint jedoch, dass alle Verwendungen für schwache Verweise lange nach dem Hinzufügen zur Sprache kamen, da schwache Verweise seit Version 1.1 Teil von .NET sind. Es scheint nur etwas zu sein, das Sie hinzufügen möchten, damit Sie das Fehlen deterministischer Zerstörung sprachlich nicht in den Hintergrund drängen.
quelle
WeakReference
Typ betrifft , da die Situation sehr viel komplexer ist. Es verwendet verschiedene Funktionen, die von der CLR bereitgestellt werden.Wenn Sie die Cache-Ebene mit C # implementiert haben, ist es auch viel besser, Ihre Daten als schwache Referenzen in den Cache zu stellen. Dies könnte dazu beitragen, die Leistung der Cache-Ebene zu verbessern.
Denken Sie, dass dieser Ansatz auch auf die Implementierung von Sitzungen angewendet werden könnte. Da die Sitzung die meiste Zeit ein langlebiges Objekt ist, kann es vorkommen, dass Sie keinen Speicher für neue Benutzer haben. In diesem Fall ist es viel besser, ein anderes Benutzersitzungsobjekt zu löschen, als OutOfMemoryException auszulösen.
Wenn Sie ein großes Objekt in Ihrer Anwendung haben (eine große Nachschlagetabelle usw.), sollte dies sehr selten verwendet werden, und die Neuerstellung eines solchen Objekts ist keine sehr teure Prozedur. Dann ist es eine gute Woche, wenn Sie die Möglichkeit haben, Ihr Gedächtnis freizugeben, wenn Sie das wirklich brauchen.
quelle