Wann würden Sie eine WeakHashMap oder eine WeakReference verwenden?

163

Die Verwendung schwacher Referenzen ist etwas, von dem ich noch nie eine Implementierung gesehen habe. Daher versuche ich herauszufinden, was der Anwendungsfall für sie ist und wie die Implementierung funktionieren würde. Wann mussten Sie ein WeakHashMapoder verwenden WeakReferenceund wie wurde es verwendet?

18Rabbit
quelle

Antworten:

96

Ein Problem bei starken Referenzen ist das Caching, insbesondere bei sehr großen Strukturen wie Bildern. Angenommen, Sie haben eine Anwendung, die mit vom Benutzer bereitgestellten Bildern arbeiten muss, wie das Website-Design-Tool, an dem ich arbeite. Natürlich möchten Sie diese Bilder zwischenspeichern, da das Laden von der Festplatte sehr teuer ist und Sie die Möglichkeit vermeiden möchten, zwei Kopien des (möglicherweise gigantischen) Bilds gleichzeitig im Speicher zu haben.

Da ein Bildcache verhindern soll, dass wir Bilder neu laden, wenn dies nicht unbedingt erforderlich ist, werden Sie schnell feststellen, dass der Cache immer einen Verweis auf ein Bild enthalten sollte, das sich bereits im Speicher befindet. Bei normalen starken Referenzen zwingt diese Referenz das Bild jedoch dazu, im Speicher zu bleiben. Daher müssen Sie irgendwie feststellen, wann das Bild nicht mehr im Speicher benötigt wird, und es aus dem Cache entfernen, damit es für die Speicherbereinigung in Frage kommt. Sie müssen das Verhalten des Garbage Collectors duplizieren und manuell festlegen, ob sich ein Objekt im Speicher befinden soll oder nicht.

Schwache Referenzen verstehen , Ethan Nicholas

Jacob Krall
quelle
43
Wäre SoftReferences in diesem Fall nicht besser, dh Referenzen, die nur erfasst werden, wenn der Speicher knapp wird.
JesperE
Ich bin ein bisschen verwirrt ... Nehmen wir an, ich habe einen SWT-Bildcache. SWT-Bilder müssen über die dispose () -Methode entsorgt werden, um SO-Ressourcen freizugeben. Wenn ich eine WeakHashMap verwende, um sie zu speichern, zeigen Sie genau, ob der GC das Objekt entsorgt.
marcolopes
2
@marcolopes Der GC würde einen Finalizer für jedes andere Objekt verwenden. Es scheint, dass SWT es nicht mag, wenn Sie das tun, also denke ich nicht, dass Sie Betriebssystemressourcen mit einem verwalten können WeakHashMap.
Jacob Krall
@marcolopes, (Ich gehe davon aus, dass Ihr GC einen Aufruf zum Abschluss garantiert, bevor er Speicher zurückfordert.) Wenn die Entsorgung im Cache-Finalizer erfolgt, ist alles in Ordnung. Wenn dispose etwas ist, das Sie manuell aufrufen müssen, dann entweder 1) erweitern Sie die Klasse und setzen Sie dispose in den Finalizer, oder 2) verwenden Sie Phantomreferenz, um dispose entsprechend zu verfolgen und auszuführen. Option 2 ist besser (vermeidet Auferstehungsfehler und gibt Ihnen die Möglichkeit, dispose auf einem anderen Thread auszuführen), aber Option 1 ist ohne Hilfsklassen einfacher zu implementieren.
Pacerier
55

WeakReference gegen SoftReference

Ein klarer Unterschied ist der Unterschied zwischen a WeakReferenceund a SoftReference.

Grundsätzlich a WeakReference wird a von der JVM eifrig GC-d sein , sobald das referenzierte Objekt keine festen Verweise darauf hat. Ein SoftReferenced-Objekt hingegen wird in der Regel vom Garbage Collector zurückgelassen, bis der Speicher wirklich wiederhergestellt werden muss.

Ein Cache, in dem die Werte gespeichert sindWeakReference s wäre ziemlich nutzlos (in a WeakHashMapsind es die Schlüssel, auf die schwach verwiesen wird). SoftReferencessind nützlich, um die Werte zu umbrechen, wenn Sie einen Cache implementieren möchten, der mit dem verfügbaren Speicher wachsen und schrumpfen kann.

oxbow_lakes
quelle
4
"Ein Cache, in dem die Werte in WeakReferences gespeichert sind, wäre ziemlich nutzlos", stimme ich überhaupt nicht zu.
Thomas Eding
5
@trinithis - ähm, ich weiß nicht wirklich was ich sagen soll. Warum ist ein Cache, dessen Werte verschwinden, sobald Sie nicht auf sie verweisen , genau eine nützliche Sache ?
oxbow_lakes
4
Für so etwas wie Memoisierung kann ein Cache nützlich sein, der seine zwischengespeicherten Werte lose enthält.
Thomas Eding
5
@ ThomasEding Ich verstehe es immer noch nicht. Ein Cache scheint nur dann nützlich zu sein, wenn keine anderen Verweise darauf vorhanden sind ... Wenn Sie Verweise darauf haben, wofür benötigen Sie einen Cache?
Cruncher
2
@ThomasEding, Softref teilt der Umgebung mit, "speichere dies, bis du keinen Speicher mehr hast". Weakref teilt der Umgebung mit, dass "dies gespeichert wird, bis GC ausgeführt wird". Es gibt offen gesagt keinen Anwendungsfall für Schwachstellen, es sei denn, Sie debuggen / profilieren den GC selbst. Wenn Sie einen speichersensitiven Cache möchten, verwenden Sie softref. Wenn Sie keinen Cache möchten, zwischenspeichern Sie ihn nicht! Wo kommt Schwachstelle ins Spiel?
Pacerier
30

Eine häufige Verwendung von WeakReferences undWeakHashMap s ist insbesondere das Hinzufügen von Eigenschaften zu Objekten. Gelegentlich möchten Sie einem Objekt einige Funktionen oder Daten hinzufügen, aber Unterklassen und / oder Kompositionen sind keine Option. In diesem Fall ist es naheliegend, eine Hashmap zu erstellen, die das Objekt, das Sie erweitern möchten, mit der Eigenschaft verknüpft, die Sie hinzufügen möchten . Wenn Sie die Immobilie benötigen, können Sie sie einfach in der Karte nachschlagen. Wenn jedoch die Objekte, die Sie hinzufügen, häufig zerstört und erstellt werden, können viele alte Objekte in Ihrer Karte viel Speicherplatz beanspruchen.

Wenn Sie WeakHashMapstattdessen a verwenden, verlassen die Objekte Ihre Karte, sobald sie vom Rest Ihres Programms nicht mehr verwendet werden. Dies ist das gewünschte Verhalten.

Ich musste dies tun, um einige Daten hinzuzufügen java.awt.Component, um eine Änderung in der JRE zwischen 1.4.2 und 1.5 zu umgehen. Ich hätte es beheben können, indem ich jede Komponente, an der ich interessiert JButtonwar JFrame, in Unterklassen unterteilt hätte (,,, JPanel....), aber das war viel einfacher mit viel weniger Code.

Luke
quelle
1
Woher weiß die WeakHashMap, dass sie vom Rest Ihres Programms nicht mehr verwendet werden?
Vinoth Kumar CM
2
Eine schwache Hashmap verwendet schwache Referenzen für ihre Schlüssel. Wenn auf ein Objekt nur durch schwache Referenzen verwiesen wird, benachrichtigt der Garbage Collector den Eigentümer über die schwache Referenz (in diesem Fall die WeaHashMap). Ich würde WeakReferences und ReferenceQueues in den Javadocs nachlesen, um zu verstehen, wie diese Objekte mit dem Garbage Collector interagieren.
Luke
1
danke luke, kannst du einen einfachen code für deine obige beschreibung bereitstellen?
gekochtes Wasser
Daher ist eine schwache Referenz in Java nur aufgrund der skurrilen API von Java sinnvoll, in der einige Klassen nicht erweitert werden können.
Pacerier
22

Ein weiterer nützlicher Fall für WeakHashMapund WeakReferenceist eine Listener-Registrierungsimplementierung .

Wenn Sie etwas erstellen, das bestimmte Ereignisse abhören möchte, registrieren Sie normalerweise einen Listener, z

manager.registerListener(myListenerImpl);

Wenn der managerSpeicher Ihren Listener mit a speichert WeakReference, bedeutet dies, dass Sie das Register nicht entfernen müssen, z. B. mit a, manager.removeListener(myListenerImpl)da es automatisch entfernt wird, sobald Ihr Listener oder Ihre Komponente, die den Listener enthält, nicht mehr verfügbar ist.

Natürlich können Sie Ihren Listener immer noch manuell entfernen, aber wenn Sie dies nicht tun oder vergessen, führt dies nicht zu einem Speicherverlust und verhindert nicht, dass Ihr Listener Müll sammelt.

Wo kommt WeakHashMapdas ins Bild?

Die Listener-Registrierung, die registrierte Listener als WeakReferences speichern möchte, benötigt eine Sammlung zum Speichern dieser Referenzen. Es gibt keine WeakHashSetImplementierung in der Standard-Java-Bibliothek nur a, WeakHashMapaber wir können die letztere leicht verwenden, um die Funktionalität der ersten zu "implementieren":

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

Damit wird listenerSetein neues Listener registrieren müssen Sie nur an die Set hinzuzufügen, und auch wenn es ausdrücklich nicht entfernt wird, wenn der Hörer wird nicht mehr Bezug genommen wird , wird sie automatisch von der JVM entfernt werden.

icza
quelle
10
Das Problem bei der Verwendung von schwachHashSets für die Listener-Liste besteht darin, dass in register () erstellte anonyme Listener-Instanzen leicht verloren gehen, was für den Benutzer unerwartet wäre. Es ist sicherer, stattdessen stärkere Verweise auf Zuhörer von Managern zu haben und sich darauf zu verlassen, dass der Anrufer stattdessen das Richtige tut.
Gunanaresh
Für die Implementierung der Listener-Registrierung: Alle registrierten Listener werden beim nächsten GC-Kick gesammelt / zerstört. Was passiert zum Beispiel, wenn GC ausgelöst wird, während alle onSomethingHappened () -Methoden aller Listener ausgelöst werden?
Blackkara
@icza, ich kann nicht glauben, dass die Leute diesen Mythos immer noch verkaufen. Dies ist eine völlig falsche Antwort. Sie können auch sagen, dass ein weiterer nützlicher Fall darin WeakHashMapbesteht, wann immer Sie ein HashMapObjekt benötigen . Wow, Sie müssen hashmap.remove nie manuell ausführen , da Elemente automatisch entfernt werden, sobald das Objekt außerhalb des Gültigkeitsbereichs liegt! Buchstäblich Magie! Solch ein hässlicher magischer Hack ist eine komplette Gesichtspalme .
Pacerier
3
@Pacerier: Ich habe Ihre Links aus anderen Kommentaren in JavaScript bis hierher verfolgt und verstehe immer noch nicht ganz, warum die Implementierung der Listener-Registrierung mit WeakMap ein Mythos ist. Wenn WebSocket-Clients beispielsweise über den Registrierungsdienst an einige Listener gebunden werden sollen, erscheint es logisch, Socket-Objekte als Schlüssel in WeakMap zu speichern (um zu verhindern, dass sie nach dem Schließen der Verbindung beispielsweise bei einem Fehler im Speicher hängen bleiben) und in der Lage sind um bei Bedarf alle ihre Zuhörer abzurufen. Könnten Sie bitte sagen, was genau an diesem Ansatz falsch ist?
Beschädigte Bio
1
@ Pacerier Auch ich verstehe Ihren Einwand nicht. In einem Publish-Subscribe- oder Event-Bus-Szenario ist eine Sammlung schwacher Referenzen für mich absolut sinnvoll. Das abonnierende Objekt darf den Gültigkeitsbereich verlassen und zur Speicherbereinigung gehen, ohne dass eine formelle Abmeldung erforderlich ist. Dieser Vorgang des Abbestellens kann besonders kompliziert sein, wenn ein Objekt eines Drittanbieters ohne Wissen des abonnierenden Objekts für das Erstabonnement verantwortlich war. Eine Sammlung von WeakReferencevereinfacht die Codebasis erheblich und vermeidet unnötige Fehler im Zusammenhang mit dem fehlgeschlagenen Abbestellen. Was für ein Nachteil?
Basil Bourque
5

Dieser Blog-Beitrag demonstriert die Verwendung beider Klassen: Java: Synchronisieren auf einer ID . Die Verwendung geht ungefähr so:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProvider bietet ID-basierte Objekte zum Synchronisieren. Die Anforderungen sind:

  • muss einen Verweis auf dasselbe Objekt zurückgeben, wenn gleichwertige IDs gleichzeitig verwendet werden
  • muss ein anderes Objekt für verschiedene IDs zurückgeben
  • Kein Freigabemechanismus (Objekte werden nicht an den Anbieter zurückgegeben)
  • darf nicht auslaufen (nicht verwendete Objekte können zur Speicherbereinigung verwendet werden)

Dies wird mithilfe einer internen Speicherkarte vom Typ erreicht:

WeakHashMap<Mutex, WeakReference<Mutex>>

Das Objekt ist sowohl Schlüssel als auch Wert. Wenn nichts außerhalb der Karte einen harten Verweis auf das Objekt hat, kann es Müll gesammelt werden. Die Werte in der Karte werden mit harten Referenzen gespeichert, daher muss der Wert in eine WeakReference eingeschlossen werden , um einen Speicherverlust zu verhindern. Dieser letzte Punkt wird im Javadoc behandelt .

McDowell
quelle
3

Wenn Sie beispielsweise alle Objekte verfolgen möchten, die von einer bestimmten Klasse erstellt wurden. Damit diese Objekte weiterhin mit Müll gesammelt werden können, führen Sie anstelle der Objekte selbst eine Liste / Karte mit schwachen Verweisen auf die Objekte.

Wenn mir jetzt jemand Phantomreferenzen erklären könnte, wäre ich glücklich ...

JesperE
quelle
2
Eine Verwendung: Mit PhantomReferences können Sie genau bestimmen, wann ein Objekt aus dem Speicher entfernt wurde. Sie sind in der Tat der einzige Weg, dies festzustellen. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall
Eigentlich wird es erst entfernt, wenn Sie es explizit löschen. "Im Gegensatz zu weichen und schwachen Referenzen werden Phantomreferenzen nicht automatisch vom Garbage Collector gelöscht, wenn sie in die Warteschlange gestellt werden. Ein Objekt, das über Phantomreferenzen erreichbar ist, bleibt so lange bestehen, bis alle diese Referenzen gelöscht werden oder selbst nicht mehr erreichbar sind."
jontro
@jontro, aber es wurde bereits abgeschlossen , alle Mitglieder sind weg. Tatsächlich ist es ein leeres Objekt. Siehe stackoverflow.com/q/7048767/632951
Pacerier
3

Wie oben erwähnt, wird eine schwache Referenz so lange gehalten, wie eine starke Referenz existiert.

Eine beispielhafte Verwendung wäre die Verwendung von WeakReference in Listenern, sodass die Listener nicht mehr aktiv sind, sobald der Hauptverweis auf ihr Zielobjekt weg ist. Beachten Sie, dass dies nicht bedeutet, dass die WeakReference aus der Listener-Liste entfernt wird. Eine Bereinigung ist weiterhin erforderlich, kann jedoch beispielsweise zu festgelegten Zeiten durchgeführt werden. Dies hat auch den Effekt, dass verhindert wird, dass das abgehörte Objekt starke Referenzen enthält und schließlich eine Quelle des Aufblähens des Gedächtnisses ist. Beispiel: Swing-GUI-Komponenten, die auf ein Modell verweisen, das einen längeren Lebenszyklus als das Fenster hat.

Während wir wie oben beschrieben mit Zuhörern spielten, stellten wir schnell fest, dass Objekte aus Sicht eines Benutzers "sofort" gesammelt werden.

Louis Jacomet
quelle
Danke nützliche Antwort. Aber ich frage mich in diesem Fall, ob Hörer stark registriert (referenziert) werden sollten.
Blackkara
Diese Antwort ist völlig falsch. Ausarbeitung: stackoverflow.com/questions/154724/…
Pacerier
@ Pacerier - denn WeakReferencesdein Kommentar ist völlig falsch!
2

Eine reale Verwendung, die ich für WeakReferences hatte, ist, wenn Sie ein einzelnes, sehr großes Objekt haben, das selten verwendet wird. Sie möchten es nicht im Speicher behalten, wenn es nicht benötigt wird. Wenn ein anderer Thread dasselbe Objekt benötigt, möchten Sie auch nicht zwei davon im Speicher haben. Sie können irgendwo einen schwachen Verweis auf das Objekt und in den Methoden, die es verwenden, harte Verweise behalten. Wenn beide Methoden abgeschlossen sind, wird das Objekt gesammelt.


quelle
1
Dies ist eine weiche Referenz, keine schwache Referenz. Siehe stackoverflow.com/a/155492/632951
Pacerier
-1

Mit schwachem Hashmap können Sie ein ressourcenfreies Caching für die umfangreiche Objekterstellung implementieren.

Beachten Sie jedoch, dass es nicht wünschenswert ist, veränderbare Objekte zu haben. Ich habe es verwendet, um Abfrageergebnisse (deren Ausführung etwa 400 ms dauert) in einer Textsuchmaschine zwischenzuspeichern, die selten aktualisiert wird.

Andreas Petersson
quelle
Sie sprechen von einer Softreferenz, nicht von einer Schwachreferenz. Siehe stackoverflow.com/a/155492/632951
Pacerier