Minor GC, Major GC, Full GC : des mots qu’on lit partout, mais pas toujours avec le sens que leur donne la documentation officielle.
La plupart des objets meurent jeunes. La JVM range donc les objets récents dans la Young, et ceux qui durent dans la Old. Un GC qui ne nettoie que la Young est rapide, et il passe souvent.
Pourquoi des générations
Le guide officiel d’Oracle sur le garbage collector part d’une observation : la plupart des objets ne vivent que très peu de temps. On l’appelle l’hypothèse générationnelle. Un objet créé pour une requête, une boucle ou un calcul devient inutile presque tout de suite.
Si la JVM sépare les objets récents des objets qui durent, elle peut nettoyer souvent la petite zone des objets récents, où presque tout est mort, sans parcourir tout le heap à chaque fois.
Le trajet d’un objet : Eden, Survivor, Old
La Young est faite de trois zones : l’Eden et deux Survivor.
- Un objet naît presque toujours dans l’Eden.
- Quand la Young est pleine, le GC nettoie la Young. Les objets encore vivants sont copiés dans un Survivor ; l’Eden est alors vide.
- Au GC suivant, les objets vivants passent d’un Survivor à l’autre. Un des deux Survivor est toujours vide.
- Après un certain nombre de copies, ou s’il n’y a plus de place dans les Survivor, l’objet est copié dans la Old : on dit qu’il est promu.
Le nombre maximum de copies se règle avec -XX:MaxTenuringThreshold.
Sa valeur par défaut est 15.
Minor, Major, Full : ce que dit vraiment la doc
Dans le guide d’Oracle :
- une minor collection ne nettoie que la Young ;
- une major collection nettoie tout le heap, quand la Old est pleine. Elle dure en général bien plus longtemps ;
- le mot full GC apparaît, par exemple, pour un nettoyage complet
demandé par
System.gc().
Beaucoup d’articles disent autre chose : « Major GC = la Old seulement, Full GC = tout le heap ». Ce n’est pas la définition du guide officiel.
Et dans les logs, les mots dépendent du GC. G1, le GC par défaut, n’écrit jamais « minor » ni « major » :
[0.238s] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 35M->3M(64M) 2.406ms
[0.287s] GC(34) Pause Young (Mixed) (G1 Evacuation Pause) 60M->35M(64M) 0.215ms
[0.335s] GC(82) Pause Full (System.gc()) 53M->27M(64M) 3.010ms
Une pause Young (Normal) nettoie la Young. Une pause Young
(Mixed) nettoie la Young et, en plus, une partie de la Old. ZGC, lui,
écrit bien Minor Collection et Major Collection :
[0.246s] GC(3) Minor Collection (Allocation Rate) 52M(81%)->52M(81%) 0.008s
[0.503s] GC(152) Major Collection (System.gc()) 38M(59%)->30M(47%) 0.009s
Quand on lit un log, il faut donc d’abord savoir quel GC l’a écrit.
Quand G1 fait un Full GC
Avec G1, le Full GC est une solution de secours : il compacte tout le heap, et la doc prévient qu’il peut être très lent. Il arrive dans deux cas principaux :
- le heap est trop plein : l’application crée des objets plus vite que G1 ne peut libérer de la place dans la Old ;
- quelqu’un le demande : un appel à
System.gc(), dans l’application ou dans un outil.
Un Full GC de temps en temps n’est pas grave. Des Full GC qui se répètent sont un signal : le heap est trop petit, ou il y a une fuite mémoire.
Ce qui a changé
Depuis Java 9, G1 est le GC par défaut. Il ne coupe plus le heap en deux grands blocs : il le découpe en régions de même taille (4 Mo sur une machine de 32 Go). La Young et la Old sont des ensembles de régions, qui ne se suivent pas forcément en mémoire.
| Ce qu’on lit encore | Ce qui est vrai aujourd’hui | Depuis |
|---|---|---|
| Le heap est coupé en deux blocs fixes, la Young et la Old | Avec G1, le heap est fait de régions de même taille ; la Young et la Old sont des ensembles de régions | Java 9 (G1 par défaut) |
| ZGC n’a pas de générations | ZGC est générationnel : en option en Java 21, par défaut en Java 23, seul mode depuis Java 24 (-XX:+ZGenerational est ignoré, avec un avertissement) | Java 21 à 24 |
| Major GC = nettoyage de la Old | Dans le guide d’Oracle, une major collection nettoie tout le heap | — |
On règle la Young avec -Xmn ou -XX:NewRatio | Avec G1, la doc conseille de ne pas le faire : cela désactive presque le contrôle des pauses | — |
Sources : le guide d’Oracle sur le garbage collector (JDK 25), JEP 248 (G1 par défaut), JEP 439, JEP 474 et JEP 490 (ZGC générationnel).
Les options, en bref
-Xmn: la taille de la Young (initiale et maximale) ;-XX:NewRatio: le rapport entre la Old et la Young (2 par défaut : la Old est deux fois plus grande que la Young) ;-XX:SurvivorRatio: le rapport entre l’Eden et un Survivor (8 par défaut) ;-XX:MaxTenuringThreshold: le nombre de copies avant la promotion (15 par défaut).
Avec G1, mieux vaut ne pas fixer la taille de la Young : G1 l’ajuste lui-même pour tenir son objectif de pause.
En résumé
Les objets naissent dans l’Eden, survivent quelques GC dans les Survivor, et les plus durables finissent dans la Old. « Minor » veut dire la Young ; « Major », dans la doc d’Oracle, veut dire tout le heap. Avec G1, on parle plutôt de pauses Young, Mixed et Full, et le heap est fait de régions.