Sur le garbage collector Java, on trouve beaucoup d’articles, et
beaucoup sont anciens : PermGen, CMS, -XX:+PrintGCDetails… Ce guide
résume ce qui est vrai en Java 25, en quelques lignes par sujet.
Chaque partie renvoie à un article plus complet, où chaque fait est
vérifié dans la documentation officielle ou sur une vraie JVM.
Je mets ce guide à jour quand la série avance, ou quand une nouvelle version de Java change les règles.
La mémoire de la JVM
La JVM range ses données dans plusieurs zones :
- la stack : une par thread, avec une frame par appel de méthode
(paramètres, variables locales). Elle se libère toute seule, sans le
GC. Trop d’appels empilés donnent un
StackOverflowError; - le heap : les objets créés avec
new, partagés par tous les threads. C’est la zone du garbage collector. Sa taille maximale se règle avec-Xmx; - le Metaspace : la description des classes, en mémoire native, hors du heap. Il a remplacé PermGen en Java 8.
La JVM utilise aussi de la mémoire native pour les stacks des threads,
le code compilé par le JIT, les buffers directs et ses structures
internes. -Xmx ne limite que le heap : le processus Java utilise
toujours plus.
→ La mémoire de la JVM : Heap, Stack, Metaspace
Générations et cycles
La plupart des objets meurent jeunes. La JVM range donc les objets récents dans la Young (l’Eden et deux Survivor) et ceux qui durent dans la Old :
Un objet naît dans l’Eden, passe d’un Survivor à l’autre à chaque GC, puis il est promu dans la Old après un certain nombre de copies (15 au maximum par défaut).
Dans le guide d’Oracle, une minor collection ne nettoie que la Young, et une major collection nettoie tout le heap. G1, le GC par défaut, n’utilise pas ces mots dans ses logs : il parle de pauses Young, Mixed et Full. Et depuis Java 9, il découpe le heap en régions de même taille, au lieu de deux grands blocs.
→ Générations et cycles de GC : Young, Old, Minor, Major, Full
Les garbage collectors
Java 25 propose plusieurs GC, chacun pour un besoin :
- G1, le GC par défaut depuis Java 9 : quand le temps de réponse compte plus que le débit ;
- ZGC et Shenandoah : des pauses très courtes, pour la latence ;
- Parallel : le débit avant tout ;
- Serial : un seul thread, pour les petites machines ;
- Epsilon : ne libère jamais rien, pour des tests.
CMS n’existe plus depuis Java 14. Attention aussi au choix automatique :
sur une machine avec un seul processeur ou moins de 1792 Mo de mémoire,
la JVM prend Serial au lieu de G1. Un petit conteneur peut donc tourner
avec un autre GC que le poste du développeur. Pour le vérifier :
-Xlog:gc écrit le GC utilisé dès la première ligne (Using G1).
→ Les garbage collectors Java en 2026 : lequel tourne chez vous ?
Les options utiles
Quelques options suffisent dans la plupart des cas :
-Xmset-Xmx: la taille initiale et maximale du heap, souvent la même valeur sur un serveur ;-Xlog:gc*:file=gc.log: les logs du GC dans un fichier, avec une rotation déjà intégrée ;-XX:+HeapDumpOnOutOfMemoryErroret-XX:HeapDumpPath: un dump du heap quand il est plein ;-XX:MaxRAMPercentage: le heap maximum en pourcentage de la mémoire (25 % par défaut), utile dans un conteneur.
Beaucoup d’anciennes options ne marchent plus. Certaines sont
seulement dépréciées (-XX:+PrintGCDetails, -Xloggc), mais d’autres
empêchent la JVM de démarrer en Java 17, 21 et 25 :
-XX:MaxPermSize, -XX:+UseConcMarkSweepGC, -XX:+PrintGCDateStamps…
Pour tester une configuration, il suffit de lancer
java <options> -version.
→ Les options JVM qui comptent, et celles qui ne marchent plus
Les idées reçues
System.gc()n’est qu’une demande. Mais avec G1, Parallel et Serial, elle déclenche en général un Full GC, qui coûte cher.finalize()est dépréciée et prévue pour suppression : à la place, try-with-resources ouCleaner.- Plus de heap aide souvent, tant qu’il tient dans la mémoire de la machine ou du conteneur.
- Régler le GC n’est pas la première chose à faire : G1 avec ses réglages par défaut, puis les logs, puis l’application.
- Le GC n’empêche pas les fuites mémoire : un objet encore référencé n’est jamais libéré.
→ Idées reçues sur le garbage collector Java
FAQ
Quelle différence entre le heap et la stack ?
La stack appartient à un thread : elle contient les variables locales et se vide quand la méthode se termine. Le heap est partagé par tous les threads : il contient les objets, et c’est le GC qui le nettoie. Une variable locale de type objet est dans la stack, mais l’objet lui-même est dans le heap.
Quel est le GC par défaut en Java 25 ?
G1, sur une machine « server-class » : au moins deux processeurs et au moins 1792 Mo de mémoire. Sinon, la JVM choisit Serial.
Peut-on forcer le GC ?
Non. System.gc() est une demande, sans garantie. Avec G1, Parallel et
Serial, elle donne en général un Full GC. Avec
-XX:+DisableExplicitGC, la JVM ignore ces appels.
Pourquoi mon application se fige quelques instants ?
Les pauses du GC sont une cause possible, pas la seule. Pour le savoir,
regardez les logs du GC (-Xlog:gc*) : chaque pause y est écrite avec
sa durée. Avec G1, un Pause Full qui ne vient pas de System.gc() est
le signe d’un heap trop plein, et la doc prévient qu’il peut être très
lent. Des Full GC qui se répètent
indiquent un heap trop petit ou une fuite mémoire.
Quel GC pour une application Spring Boot ?
Spring Boot ne choisit pas le GC : c’est la JVM, avec les mêmes règles
que pour toute application Java. Une application Spring Boot 3.5 sur
Java 25 démarre avec G1, et avec Serial si la JVM ne voit qu’un seul
processeur. Commencez avec G1 et ses réglages par défaut, et vérifiez le
GC utilisé avec -Xlog:gc.
La suite de la série
Ce guide couvre les bases. La suite de la série ira plus loin :
- comprendre et diagnostiquer :
les variantes de
OutOfMemoryError, les fuites mémoire, la lecture des logs du GC, les outils (JFR, jcmd, VisualVM) et l’analyse d’un heap dump ; - mesurer et régler : Java dans Docker et Kubernetes, la mémoire hors heap, G1 ou ZGC mesurés, le réglage d’un service Spring Boot et Kafka, et le GC avec les threads virtuels.
Ces articles seront ajoutés à ce guide au fur et à mesure.