Anleitung zu WeakHashMap in Java

Anleitung zu WeakHashMap in Java

1. Überblick

In diesem Artikel betrachten wir einWeakHashMap aus demjava.util-Paket.

Um die Datenstruktur zu verstehen, verwenden wir sie hier, um eine einfache Cache-Implementierung einzuführen. Bedenken Sie jedoch, dass dies dazu gedacht ist, die Funktionsweise der Karte zu verstehen, und dass das Erstellen einer eigenen Cache-Implementierung fast immer eine schlechte Idee ist.

Einfach ausgedrückt istWeakHashMap eine auf Hashtabellen basierende Implementierung derMap-Schnittstelle mit Schlüsseln vom TypWeakReference.

Ein Eintrag inWeakHashMap wird automatisch entfernt, wenn sein Schlüssel nicht mehr normal verwendet wird. Dies bedeutet, dass keine einzelnenReference auf diesen Schlüssel verweisen. Wenn der Garbage Collection (GC) -Prozess einen Schlüssel verwirft, wird sein Eintrag effektiv aus der Map entfernt, sodass sich diese Klasse etwas anders verhält als andereMap-Implementierungen.

2. Starke, weiche und schwache Referenzen

Um zu verstehen, wieWeakHashMap funktioniert, istwe need to look at a WeakReference class - das Grundkonstrukt für Schlüssel in der Implementierung vonWeakHashMap. In Java gibt es drei Haupttypen von Referenzen, die wir in den folgenden Abschnitten erläutern werden.

2.1. Starke Referenzen

Die starke Referenz ist der häufigste Typ vonReference, den wir in unserer täglichen Programmierung verwenden:

Integer prime = 1;

Die Variableprime hat einstrong reference zu einemInteger-Objekt mit dem Wert 1. Objekte, auf die stark verwiesen wird, sind nicht für die GC geeignet.

2.2. Weiche Referenzen

Einfach ausgedrückt, ein Objekt, auf dasSoftReferencezeigt, wird erst dann mit Müll gesammelt, wenn die JVM unbedingt Speicher benötigt.

Mal sehen, wie wir in Java einSoftReference erstellen können:

Integer prime = 1;
SoftReference soft = new SoftReference(prime);
prime = null;

Das Objektprimehat einen starken Verweis darauf.

Als nächstes verpacken wir die starke Referenz vonprimein eine weiche Referenz. Nachdem Sie diese starke Referenznull erstellt haben, ist einprime-Objekt für die GC geeignet, wird jedoch nur erfasst, wenn JVM unbedingt Speicher benötigt.

2.3. Schwache Referenzen

Die Objekte, auf die nur durch schwache Referenzen verwiesen wird, sind eifrig gesammelter Müll; Der GC wartet in diesem Fall nicht, bis er Speicher benötigt.

Wir können einWeakReference in Java folgendermaßen erstellen:

Integer prime = 1;
WeakReference soft = new WeakReference(prime);
prime = null;

Wenn wir eineprime-Referenznull erstellt haben, wird dasprime-Objekt im nächsten GC-Zyklus als Müll gesammelt, da es keine andere starke Referenz gibt, die darauf hinweist.

Referenzen vom TypWeakReference werden als Schlüssel inWeakHashMap verwendet.

3. WeakHashMap als effizienter Speichercache

Angenommen, wir möchten einen Cache erstellen, in dem große Bildobjekte als Werte und Bildnamen als Schlüssel gespeichert werden. Wir möchten eine geeignete Kartenimplementierung auswählen, um dieses Problem zu lösen.

Die Verwendung eines einfachenHashMap ist keine gute Wahl, da die Wertobjekte möglicherweise viel Speicher belegen. Darüber hinaus werden sie von einem GC-Prozess niemals aus dem Cache zurückgefordert, selbst wenn sie in unserer Anwendung nicht mehr verwendet werden.

Idealerweise möchten wir eineMap-Implementierung, mit der GC nicht verwendete Objekte automatisch löschen kann. Wenn ein Schlüssel eines großen Bildobjekts in unserer Anwendung an keiner Stelle verwendet wird, wird dieser Eintrag aus dem Speicher gelöscht.

Glücklicherweise hatWeakHashMap genau diese Eigenschaften. Testen wir unsereWeakHashMap und sehen wir, wie sie sich verhalten:

WeakHashMap map = new WeakHashMap<>();
BigImage bigImage = new BigImage("image_id");
UniqueImageName imageName = new UniqueImageName("name_of_big_image");

map.put(imageName, bigImage);
assertTrue(map.containsKey(imageName));

imageName = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS).until(map::isEmpty);

Wir erstellen eineWeakHashMap-Instanz, in der dieBigImage-Objekte gespeichert werden. Wir setzen einBigImage-Objekt als Wert und eineimageName-Objektreferenz als Schlüssel. DieimageName werden in einer Karte alsWeakReference-Typ gespeichert.

Als nächstes setzen wir die ReferenzimageNameaufnull, daher gibt es keine Referenzen mehr, die auf das ObjektbigImageverweisen. Das Standardverhalten vonWeakHashMap besteht darin, einen Eintrag zurückzugewinnen, auf den beim nächsten GC kein Verweis verweist, sodass dieser Eintrag beim nächsten GC-Prozess aus dem Speicher gelöscht wird.

Wir rufenSystem.gc() auf, um die JVM zu zwingen, einen GC-Prozess auszulösen. Nach dem GC-Zyklus sind unsereWeakHashMap leer:

WeakHashMap map = new WeakHashMap<>();
BigImage bigImageFirst = new BigImage("foo");
UniqueImageName imageNameFirst = new UniqueImageName("name_of_big_image");

BigImage bigImageSecond = new BigImage("foo_2");
UniqueImageName imageNameSecond = new UniqueImageName("name_of_big_image_2");

map.put(imageNameFirst, bigImageFirst);
map.put(imageNameSecond, bigImageSecond);

assertTrue(map.containsKey(imageNameFirst));
assertTrue(map.containsKey(imageNameSecond));

imageNameFirst = null;
System.gc();

await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.size() == 1);
await().atMost(10, TimeUnit.SECONDS)
  .until(() -> map.containsKey(imageNameSecond));

Beachten Sie, dass nur die ReferenzimageNameFirst aufnull gesetzt ist. DieimageNameSecond-Referenz bleibt unverändert. Nach dem Auslösen der GC enthält die Karte nur einen Eintrag -imageNameSecond.

4. Fazit

In diesem Artikel haben wir uns mit Referenztypen in Java befasst, um zu verstehen, wiejava.util.WeakHashMap funktioniert. Wir haben einen einfachen Cache erstellt, der das Verhalten vonWeakHashMap nutzt und testet, ob es wie erwartet funktioniert.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie inGitHub project - einem Maven-Projekt. Daher sollte es einfach zu importieren und auszuführen sein.