Sur le garbage collector, on lit encore beaucoup de conseils. Certains étaient faux dès le départ, d’autres ne sont plus vrais aujourd’hui.
Voici cinq idées reçues, et ce que disent la documentation officielle et une vraie JVM en Java 25.
« System.gc() force un GC »
Non : c’est une demande, pas un ordre. La javadoc dit que
System.gc() « suggère » à la JVM de libérer de la mémoire, sans
aucune garantie sur la quantité libérée, ni même que le travail soit
fait.
En pratique, sur Java 25, la demande est souvent suivie, et elle coûte cher :
| GC | Ce que fait System.gc() |
|---|---|
| G1 (par défaut), Parallel, Serial | Un Full GC : Pause Full (System.gc()) |
| ZGC | Une collection majeure : Major Collection (System.gc()) |
G1 avec -XX:+ExplicitGCInvokesConcurrent | Une pause young, puis un cycle concurrent |
Avec -XX:+DisableExplicitGC | Rien : l’appel est ignoré |
Avec -XX:+DisableExplicitGC, la JVM ignore les appels à
System.gc(), mais elle fait toujours un GC quand elle en a besoin.
« finalize() sert à libérer les ressources »
Non. finalize() est dépréciée depuis Java 9, et prévue pour
suppression depuis Java 18 (JEP 421).
Le GC peut l’appeler très tard, sans aucun délai garanti. Elle tourne sur un thread
quelconque, dans un ordre quelconque. Et si le thread des finalizers
n’arrive pas à suivre, les objets en attente peuvent remplir le heap,
jusqu’à l’OutOfMemoryError.
À la place :
- une méthode
close(), avecAutoCloseableet un try-with-resources : la fermeture est garantie, même en cas d’exception ; Cleaner(Java 9), pour lancer une action de nettoyage quand l’objet n’est plus accessible.
Pour vérifier qu’une application ne dépend plus des finalizers, on peut
la lancer avec --finalization=disabled (depuis Java 18).
« Plus de heap, c’est toujours mieux »
Souvent vrai, mais pas toujours. Le guide d’Oracle conseille de donner à la JVM autant de mémoire que possible, sauf si vous avez des problèmes de pauses. La taille par défaut est souvent trop petite.
Mais il y a des limites :
- le heap maximum doit rester sous la mémoire de la machine, sinon le système se met à paginer ;
- la JVM utilise aussi de la mémoire hors du heap (Metaspace, piles des threads…) : dans un conteneur, tout doit tenir dans la limite ;
- avec G1, un heap plus grand allonge en général la phase de marquage.
Les options pour régler la taille du heap sont dans l’article sur les options JVM.
« Régler le GC est la première chose à faire »
Non. Le guide d’Oracle dit de commencer par lancer l’application et de
laisser la JVM choisir le GC. Pour G1, sa recommandation générale est
de garder les réglages par défaut, avec au plus un objectif de pause
différent et un heap maximum (-Xmx).
Avant de changer une option, regardez ce qui se passe : les logs du GC
sont un bon départ (-Xlog:gc*). Et le guide le rappelle : une
optimisation dans l’application peut être plus efficace que le réglage
de la JVM.
« Le GC empêche les fuites mémoire »
Non. Le GC libère les objets que plus personne ne référence. Mais pour
Oracle, une fuite mémoire en Java, c’est justement une application qui
garde des références à des objets ou à des classes sans le vouloir : un
cache qui grossit toujours, une liste jamais vidée… Le GC ne peut pas
les libérer. Le heap se remplit, ou la Metaspace pour les classes,
jusqu’à l’OutOfMemoryError.
Et il existe aussi des fuites de mémoire native, hors du heap. Pour celles de la JVM elle-même, l’outil Native Memory Tracking (NMT) aide à les trouver.
Ce qui a changé
| Ce qu’on lit encore | Ce qui est vrai aujourd’hui | Depuis |
|---|---|---|
Libérer les ressources dans finalize() | finalize() est dépréciée, prévue pour suppression ; try-with-resources ou Cleaner à la place | Java 9 (dépréciée), Java 18 (pour suppression) |
| Une fuite de classes remplit la PermGen | La PermGen n’existe plus : c’est la Metaspace qui se remplit | Java 8 |
| Régler les options de CMS | CMS est supprimé ; G1 est le GC par défaut, et ses réglages par défaut sont recommandés | Java 9 (G1 par défaut), Java 14 (CMS supprimé) |
Sources : la javadoc de System.gc()
et de Object.finalize(),
JEP 421 (finalization),
la documentation de la commande java,
le guide d’Oracle sur le garbage collector
et le guide de dépannage sur les fuites mémoire
(JDK 25).
En résumé
System.gc() est une demande coûteuse, finalize() est à oublier, un
heap plus grand aide tant qu’il tient en mémoire, on mesure avant de
régler, et le GC ne protège pas des références oubliées.