W3docs

Java-Referenztypen: Strong, Weak, Soft, Phantom

Wie Java-Referenzstärken mit dem Garbage Collector interagieren, um Caches, Listener und Ressourcenbereinigung zu ermöglichen.

Die Garbage Collection wirkt automatisch, aber Java gibt Ihnen einen Regler, um sie zu beeinflussen. Eine gewöhnliche Variable ist eine Strong Reference, die ein Objekt im Speicher hält. Das Paket java.lang.ref fügt drei schwächere Stufen hinzu — Soft, Weak und Phantom —, die es dem Garbage Collector erlauben, ein Objekt freizugeben, auch wenn Sie noch einen Verweis darauf besitzen. Diese Referenztypen sind die Grundlage für speichersensitive Caches, leckfreie Listener-Registries und zuverlässige Ressourcenbereinigung.

Erreichbarkeit entscheidet über Leben und Tod

Der Garbage Collector hält ein Objekt am Leben, solange es von einer GC-Wurzel (einem aktiven Thread, einem statischen Feld, einer Stack-Variable) aus erreichbar ist. Die Stärke der Referenzen auf dem Pfad zu einem Objekt bestimmt seine Erreichbarkeitsklasse, und diese Klasse entscheidet über sein Schicksal, wenn Speicher benötigt wird.

Referenzget() gibt Objekt zurück?GC hält es am Leben?Typische Verwendung
StrongImmerImmer (solange stark erreichbar)Gewöhnliche Variablen und Felder
SoftBis Speicher knapp wirdBis der Heap unter Druck stehtSpeichersensitive Caches
WeakBis zum nächsten GC-DurchlaufNeinKanonisierende Maps, Listener
PhantomNie (immer null)NeinNachträgliche Bereinigung

Die Reihenfolge von stärkster zu schwächster lautet: Strong → Soft → Weak → Phantom. Wenn mehrere Referenzen unterschiedlicher Stärke auf ein Objekt zeigen, gewinnt die stärkste — eine einzige Strong Reference reicht aus, um ein Objekt dauerhaft am Leben zu erhalten.

Strong References: der Standard

Jede Referenz, die Sie ohne die java.lang.ref-API schreiben, ist stark. Solange eine Strong Reference erreichbar ist, kann das Objekt nicht eingesammelt werden — dies ist die Ursache der meisten Speicherlecks, bei denen ein vergessener Eintrag in einer langlebigen Collection Objekte unbegrenzt am Leben hält.

List<byte[]> cache = new ArrayList<>();
cache.add(new byte[10_000_000]); // 10 MB pinned by a strong reference
// Nothing here can be collected until 'cache' itself becomes unreachable.

Soft References: speichersensitive Caches

Eine SoftReference lässt den Collector das Objekt nur dann freigeben, wenn der Heap knapp wird. Solange genügend Speicher vorhanden ist, gibt get() weiterhin das Objekt zurück, was Soft References zur natürlichen Wahl für Caches macht, die bei Speicherdruck schrumpfen sollen, anstatt einen OutOfMemoryError zu verursachen.

SoftReference<BufferedImage> ref = new SoftReference<>(loadThumbnail(path));

BufferedImage cached = ref.get();
if (cached == null) {             // GC reclaimed it under memory pressure
  cached = loadThumbnail(path);   // recompute and re-wrap
  ref = new SoftReference<>(cached);
}
return cached;

Die JVM garantiert, dass alle Soft References auf softly-erreichbare Objekte geleert werden, bevor sie einen OutOfMemoryError wirft — ein Soft-Reference-Cache ist also das Letzte, das geopfert wird, nicht das Erste.

Weak References: Maps und Listener

Eine WeakReference verzögert die Einsammlung überhaupt nicht. Sobald ein Objekt nur noch schwach erreichbar ist, wird es zur GC-Kandidatin und get() gibt nach dem nächsten Collection-Zyklus null zurück. Genau das wollen Sie für Schlüssel in einem Cache, der verschwinden soll, wenn niemand sonst die Einträge mehr verwendet — das ist die Grundlage von WeakHashMap.

WeakHashMap<Widget, Metadata> sidecar = new WeakHashMap<>();
sidecar.put(widget, metadata);
// When 'widget' is no longer strongly referenced elsewhere, the entry
// vanishes automatically — no manual remove(), no leak.

Die Kombination einer Referenz mit einer ReferenceQueue lässt Sie benachrichtigt werden, wenn das referenzierte Objekt eingesammelt wurde: Der GC stellt die geleerte Referenz in die Warteschlange, damit Sie Folgelogik ausführen können.

ReferenceQueue<Widget> queue = new ReferenceQueue<>();
WeakReference<Widget> ref = new WeakReference<>(widget, queue);
// later, on a cleanup thread:
Reference<?> dead = queue.remove(); // blocks until a referent is collected

Phantom References: nachträgliche Bereinigung

Eine PhantomReference ist die schwächste Stufe und die speziellste. Ihr get() gibt immer null zurück, sodass Sie das Objekt darüber niemals wiederbeleben können. Ihr einziger Zweck ist es, nach der Einsammlung des Objekts in die Warteschlange gestellt zu werden, was Ihnen einen sicheren Hook zum Freigeben nativer Ressourcen bietet — der moderne, zuverlässige Ersatz für das veraltete finalize().

ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<>(resource, queue);
// A background thread drains the queue and frees the off-heap buffer
// only once the JVM confirms the object is truly gone.

Das eigene java.lang.ref.Cleaner des JDK (Java 9+) basiert auf Phantom References und ist das, was Sie in echtem Code verwenden sollten, anstatt die Warteschlange manuell zu verwalten.

Ein ausgearbeitetes Beispiel: alle vier Stärken in einem Durchlauf

Dieses Programm erstellt ein Objekt, das von jeder Referenzart gehalten wird, erzwingt eine Garbage Collection mit System.gc() und meldet, was überlebt hat. Es verbindet außerdem ReferenceQueues mit den Weak- und Phantom-Referenzen, damit Sie sehen können, wie der GC Sie über jeden "Tod" benachrichtigt.

java— editable, runs on the server

Was man aus dem Durchlauf mitnehmen sollte:

  • Die Strong Reference gab Resource(kept-alive) sowohl am Anfang als auch am Ende aus. Trotz zweier System.gc()-Aufrufe dazwischen ist ein stark erreichbares Objekt niemals ein Einsammlungskandidat — Strong References gewinnen immer.
  • weak.get() gab Resource(weakly-held) vor dem GC zurück, aber null danach. Sobald der einzige starke Verweis (onlyWeaklyHeld) auf null gesetzt wurde, war das Objekt nur noch schwach erreichbar, sodass die nächste Collection die Weak Reference leerte.
  • weak ref enqueued? : true bestätigt, dass der GC die geleerte WeakReference in die ReferenceQueue eingestellt hat. Dieses Einreihen ist der Benachrichtigungsmechanismus — so erfahren WeakHashMap und Listener-Registries, dass ein Eintrag bereinigt werden kann.
  • soft.get() gab nach gc() weiterhin Resource(cache-entry) zurück. Der Heap stand unter keinem Druck, sodass der Collector das softly-erreichbare Objekt am Leben hielt — genau das Verhalten, das Soft References für Caches geeignet macht, die nur bei Speicherknappheit schrumpfen.
  • phantom.get() gab null aus, noch bevor irgendeine Einsammlung stattfand, dennoch zeigt phantom enqueued? : true, dass es eingereiht wurde, sobald sein Referent starb. Eine Phantom Reference gibt das Objekt niemals zurück; sie existiert ausschließlich um zu signalisieren, nachträglich, dass die Bereinigung sicher ausgeführt werden kann.

Verwandte Themen

Referenzstärken ergeben nur im Zusammenhang damit Sinn, wie die JVM Speicher zurückgewinnt:

  • Java Garbage Collection — was "erreichbar" bedeutet und wann der Collector tatsächlich läuft.
  • Java Memory Model — wie Objekte, Threads und Sichtbarkeit zusammenpassen.
  • Java Stack vs Heap — wo die Objekte, auf die Ihre Referenzen zeigen, tatsächlich leben.
  • Java HashMap — das stark-verschlüsselte Gegenstück zur oben verwendeten WeakHashMap.

Übungen

Übung
Sie benötigen einen Cache, der berechnete Werte speichert, um Ihr Programm zu beschleunigen, diese aber automatisch freigeben soll, anstatt bei niedrigem Heap-Speicher einen OutOfMemoryError zu verursachen. Welcher Referenztyp passt am besten?
Sie benötigen einen Cache, der berechnete Werte speichert, um Ihr Programm zu beschleunigen, diese aber automatisch freigeben soll, anstatt bei niedrigem Heap-Speicher einen OutOfMemoryError zu verursachen. Welcher Referenztyp passt am besten?
Was this page helpful?