W3docs

Types de références Java : Strong, Weak, Soft, Phantom

Comment les niveaux de référence Java interagissent avec le garbage collector pour les caches, listeners et nettoyages.

Le ramasse-miettes semble automatique, mais Java vous offre un curseur pour l'influencer. Une variable ordinaire est une référence forte qui épingle un objet en mémoire. Le package java.lang.ref ajoute trois niveaux plus faibles — soft, weak et phantom — qui permettent au garbage collector de récupérer un objet même si vous en détenez encore un handle. Ces types de références constituent le fondement des caches sensibles à la mémoire, des registres de listeners sans fuite, et du nettoyage fiable des ressources.

L'accessibilité détermine qui survit

Le garbage collector maintient un objet en vie tant qu'il est accessible depuis une racine GC (un thread actif, un champ statique, une variable de pile). La force des références sur le chemin vers un objet détermine sa classe d'accessibilité, et cette classe décide de son sort lorsque de la mémoire est nécessaire.

Référenceget() retourne l'objet ?Le GC le garde en vie ?Utilisation typique
StrongToujoursToujours (tant qu'il est fortement accessible)Variables et champs ordinaires
SoftJusqu'à ce que la mémoire soit faibleJusqu'à ce que le tas soit sous pressionCaches sensibles à la mémoire
WeakJusqu'au prochain cycle GCNonMaps canoniques, listeners
PhantomJamais (toujours null)NonNettoyage post-mortem

L'ordre du plus fort au plus faible est : strong → soft → weak → phantom. Lorsque plusieurs références de différentes forces pointent vers un même objet, la plus forte l'emporte — une seule référence forte suffit à maintenir un objet en vie indéfiniment.

Références fortes : la valeur par défaut

Chaque référence que vous écrivez sans l'API java.lang.ref est forte. Tant qu'une référence forte est accessible, l'objet ne peut pas être collecté — c'est la source de la plupart des fuites mémoire, où une entrée oubliée dans une collection de longue durée maintient des objets en vie indéfiniment.

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.

Références soft : caches sensibles à la mémoire

Une SoftReference permet au collecteur de récupérer l'objet uniquement lorsque le tas est presque plein. Tant que la mémoire est abondante, get() continue de retourner l'objet, ce qui fait des références soft le choix naturel pour les caches qui doivent se réduire sous pression plutôt que provoquer une OutOfMemoryError.

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;

La JVM garantit que toutes les références soft vers des objets soft-accessibles sont effacées avant de lever une OutOfMemoryError, de sorte qu'un cache à références soft est le dernier sacrifié, pas le premier.

Références weak : maps et listeners

Une WeakReference ne retarde pas du tout la collecte. Dès qu'un objet n'est que faiblement accessible, il devient éligible au GC et get() retournera null après le prochain cycle de collecte. C'est exactement ce que vous souhaitez pour les clés d'un cache qui doit disparaître lorsque personne d'autre ne les utilise — c'est ce sur quoi WeakHashMap est construit.

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.

Associer une référence à une ReferenceQueue vous permet d'être notifié lorsque le référent est collecté : le GC met en file d'attente la référence effacée afin que vous puissiez exécuter une logique de suivi.

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

Références phantom : nettoyage post-mortem

Une PhantomReference est le niveau le plus faible et le plus spécialisé. Sa méthode get() retourne toujours null, vous ne pouvez donc jamais ressusciter l'objet par son intermédiaire. Son seul but est d'être mise en file d'attente après que l'objet a été collecté, vous offrant un point d'ancrage sûr pour libérer des ressources natives — le remplacement moderne et fiable du finalize() déprécié.

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.

Le propre java.lang.ref.Cleaner du JDK (Java 9+) est construit sur les références phantom et c'est ce que vous devriez utiliser dans du vrai code plutôt que de gérer la file d'attente manuellement.

Un exemple concret : les quatre niveaux en une seule exécution

Ce programme crée un objet détenu uniquement par chaque type de référence, force un ramasse-miettes avec System.gc(), et indique ce qui a survécu. Il connecte également des ReferenceQueues aux références weak et phantom afin que vous puissiez observer les notifications du GC à chaque suppression.

java— editable, runs on the server

Ce que l'exécution nous enseigne :

  • La référence forte a affiché le même Resource(kept-alive) au début et à la fin. Malgré deux appels System.gc() entre les deux, un objet fortement accessible n'est jamais candidat à la collecte — les références fortes l'emportent toujours.
  • weak.get() a retourné Resource(weakly-held) avant le GC mais null après. Une fois le seul lien fort (onlyWeaklyHeld) mis à null, l'objet n'était plus qu'accessiblefaiblement, et le prochain cycle de collecte a donc effacé la référence weak.
  • weak ref enqueued? : true confirme que le GC a poussé la WeakReference effacée dans sa ReferenceQueue. Cet enqueue est le mécanisme de notification — c'est ainsi que WeakHashMap et les registres de listeners apprennent qu'une entrée peut être purgée.
  • soft.get() a encore retourné Resource(cache-entry) après gc(). Le tas n'était sous aucune pression, donc le collecteur a maintenu l'objet soft-accessible en vie — exactement le comportement qui rend les références soft adaptées aux caches qui ne se réduisent que lorsque la mémoire est faible.
  • phantom.get() a affiché null même avant toute collecte, mais phantom enqueued? : true montre qu'il a quand même été mis en file d'attente une fois son référent mort. Une référence phantom ne rend jamais l'objet ; elle existe uniquement pour signaler après coup que le nettoyage peut s'exécuter en toute sécurité.

Sujets connexes

Les niveaux de référence n'ont de sens qu'au regard de la façon dont la JVM récupère la mémoire :

  • Java Garbage Collection — ce que signifie « accessible » et quand le collecteur s'exécute réellement.
  • Java Memory Model — comment les objets, les threads et la visibilité s'articulent ensemble.
  • Java Stack vs Heap — où vivent réellement les objets vers lesquels pointent vos références.
  • Java HashMap — le pendant à clés fortes de la WeakHashMap utilisée ci-dessus.

Pratique

Pratique
Vous avez besoin d'un cache qui conserve des valeurs calculées pour accélérer votre programme, mais qui doit les libérer automatiquement plutôt que de provoquer une OutOfMemoryError lorsque le tas est faible. Quel type de référence convient le mieux ?
Vous avez besoin d'un cache qui conserve des valeurs calculées pour accélérer votre programme, mais qui doit les libérer automatiquement plutôt que de provoquer une OutOfMemoryError lorsque le tas est faible. Quel type de référence convient le mieux ?
Was this page helpful?