Votre application tourne peut-être avec un autre GC que celui que vous croyez.
En Java 25, G1 est le garbage collector par défaut. Mais la JVM en propose d’autres, chacun pour un besoin. Et sur une petite machine, elle en choisit un autre toute seule.
Les GC disponibles en Java 25
| GC | Ce qu’en dit le guide d’Oracle | Option |
|---|---|---|
| Serial | Un seul thread pour tout le travail du GC. Pour un petit jeu de données (jusqu’à 100 Mo environ) ou un seul processeur | -XX:+UseSerialGC |
| Parallel | Le « throughput collector » : comme Serial, mais avec plusieurs threads. Si la performance brute compte le plus, et que des pauses d’une seconde ou plus sont acceptables | -XX:+UseParallelGC |
| G1 | Un GC « en grande partie concurrent » : il fait une partie du travail pendant que l’application tourne. Si le temps de réponse compte plus que le débit | -XX:+UseG1GC |
| ZGC | Des pauses de moins d’une milliseconde, au prix d’un peu de débit. Si la latence est la priorité | -XX:+UseZGC |
| Shenandoah | Un GC à pauses courtes, lui aussi. Le guide d’Oracle n’en parle pas ; il fonctionne avec Temurin 25 | -XX:+UseShenandoahGC |
Et un cas à part : Epsilon. Il alloue la mémoire, mais ne la libère
jamais. Quand le heap est plein, la JVM s’arrête. Il sert pour des tests
(mesurer une application sans GC, vérifier qu’un test ne crée pas trop
d’objets) ou des tâches très courtes. En Java 25, il est encore
expérimental : il faut -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC.
Le GC par défaut : G1… sauf sur une petite machine
La JVM choisit G1 sur une machine « server-class » : au moins deux processeurs et au moins 1792 Mo de mémoire physique. Sinon, elle choisit Serial.
Sur une machine de développement, c’est presque toujours G1. Mais dans un petit conteneur limité à un seul CPU, la JVM passe à Serial, sans rien dire. Le comportement de l’application peut alors changer entre le poste du développeur et la production.
Le conseil du guide d’Oracle : sauf si vous avez des contraintes de pause strictes, lancez d’abord l’application et laissez la JVM choisir.
Comment savoir quel GC tourne
Au démarrage, avec -Xlog:gc, la JVM l’écrit dès la première ligne :
[0.015s][info][gc] Using G1
Sans lancer l’application, -XX:+PrintFlagsFinal montre le choix de la
JVM pour cette machine (ergonomic veut dire « choisi par la JVM ») :
bool UseG1GC = true {product} {ergonomic}
Et pour une application déjà lancée, jcmd <pid> VM.flags affiche ses
options, avec par exemple -XX:+UseG1GC.
Ce qui a changé
| Ce qu’on lit encore | Ce qui est vrai aujourd’hui | Depuis |
|---|---|---|
| Parallel est le GC par défaut | G1 est le GC par défaut | Java 9 |
| CMS, le GC à faibles pauses | CMS est supprimé. En Java 17, 21 et 25, -XX:+UseConcMarkSweepGC empêche même la JVM de démarrer | Déprécié en Java 9, supprimé en Java 14 |
| ZGC et Shenandoah sont expérimentaux | Ils sont prêts pour la production | Java 15 |
| ZGC n’a pas de générations | ZGC est générationnel, et c’est le seul mode depuis Java 24 | Java 21 à 24 |
| Shenandoah n’a pas de générations | Son mode générationnel est stable en Java 25, mais ce n’est pas le mode par défaut (-XX:ShenandoahGCMode=generational) | Java 25 |
Sources : le guide d’Oracle sur le garbage collector (JDK 25), JEP 248 (G1 par défaut), JEP 291 et JEP 363 (CMS), JEP 377 (ZGC), JEP 379 et JEP 521 (Shenandoah), JEP 318 (Epsilon).
En résumé
G1 est le bon point de départ, et c’est celui que la JVM choisit sur une machine normale. ZGC et Shenandoah visent les pauses les plus courtes, Parallel le débit, Serial les petites machines. Avant de changer de GC, vérifiez lequel tourne vraiment, surtout dans un conteneur.