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.
| Referenz | get() gibt Objekt zurück? | GC hält es am Leben? | Typische Verwendung |
|---|---|---|---|
| Strong | Immer | Immer (solange stark erreichbar) | Gewöhnliche Variablen und Felder |
| Soft | Bis Speicher knapp wird | Bis der Heap unter Druck steht | Speichersensitive Caches |
| Weak | Bis zum nächsten GC-Durchlauf | Nein | Kanonisierende Maps, Listener |
| Phantom | Nie (immer null) | Nein | Nachträ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 collectedPhantom 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.
Was man aus dem Durchlauf mitnehmen sollte:
- Die Strong Reference gab
Resource(kept-alive)sowohl am Anfang als auch am Ende aus. Trotz zweierSystem.gc()-Aufrufe dazwischen ist ein stark erreichbares Objekt niemals ein Einsammlungskandidat — Strong References gewinnen immer. weak.get()gabResource(weakly-held)vor dem GC zurück, abernulldanach. Sobald der einzige starke Verweis (onlyWeaklyHeld) aufnullgesetzt wurde, war das Objekt nur noch schwach erreichbar, sodass die nächste Collection die Weak Reference leerte.weak ref enqueued? : truebestätigt, dass der GC die geleerteWeakReferencein dieReferenceQueueeingestellt hat. Dieses Einreihen ist der Benachrichtigungsmechanismus — so erfahrenWeakHashMapund Listener-Registries, dass ein Eintrag bereinigt werden kann.soft.get()gab nachgc()weiterhinResource(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()gabnullaus, noch bevor irgendeine Einsammlung stattfand, dennoch zeigtphantom 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.