Vous reprenez un vieux script de démarrage, vous passez à Java 25, et la JVM ne démarre plus :

Unrecognized VM option 'PrintGCDateStamps'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

Beaucoup d’options mémoire et GC qu’on trouve encore sur le web ont changé ou disparu. Certaines sont juste ignorées, d’autres empêchent la JVM de démarrer. Et les options utiles aujourd’hui tiennent en quelques lignes.

Les options qui comptent

OptionRôleQuand la changerQuand ne pas y toucher
-Xms, -XmxTaille initiale et maximale du heapPresque toujours sur un serveur : la valeur par défaut est souvent trop petite—
-XssTaille de la pile de chaque threadAprès un StackOverflowError sur une récursion légitimeSinon : la valeur par défaut suffit
-XX:+UseG1GC, -XX:+UseParallelGC, -XX:+UseZGCChoix du GCQuand un besoin précis le demande (latence, débit)Par défaut : laissez d’abord la JVM choisir
-XX:MaxGCPauseMillisObjectif de pause (G1 : 200 ms par défaut)Si les pauses de G1 sont trop longues pour vousPour « accélérer » le GC : des pauses plus courtes peuvent coûter du débit
-Xlog:gc*Logs du GCToujours utile en production, vers un fichier—
-XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPathDump du heap en cas de heap pleinEn production, pour comprendre un OutOfMemoryError—
-XX:MaxRAMPercentageHeap maximum en pourcentage de la mémoire (25 % par défaut)Dans un conteneur, à la place d’un -Xmx fixe—

La taille du heap : -Xms et -Xmx

-Xms donne la taille initiale du heap, -Xmx sa taille maximale. Sans ces options, la JVM prend 1/64 de la mémoire physique au départ et 1/4 au maximum. Sur ma machine (32 Go), cela donne 512 Mo et 8 Go.

Sur un serveur, on donne souvent la même valeur aux deux. Le guide d’Oracle explique pourquoi : c’est plus prévisible, car la JVM n’a plus à décider de la taille du heap. Mais en contrepartie, si la valeur est mauvaise, la JVM ne peut plus la corriger.

-Xss fixe la taille de la pile de chaque thread : 1 Mo par défaut sur Linux et macOS en x64, 2 Mo sur Aarch64 (les Mac Apple Silicon, par exemple).

Pauses, logs et heap dump

-XX:MaxGCPauseMillis est un objectif, pas une promesse. La documentation parle d’un « soft goal » : la JVM fait de son mieux, mais elle peut ne pas l’atteindre. Pour tenir un objectif plus court, elle peut lancer le GC plus souvent, et l’application fait alors moins de travail utile.

-Xlog:gc* écrit les logs du GC. Pour les envoyer dans un fichier : -Xlog:gc*:file=gc.log. La rotation est déjà intégrée : par défaut, 5 fichiers de 20 Mo environ, à changer avec filecount et filesize.

-XX:+HeapDumpOnOutOfMemoryError écrit un fichier .hprof quand le heap est plein. Elle est désactivée par défaut. Sans -XX:HeapDumpPath, le fichier va dans le dossier courant, avec le nom java_pid<pid>.hprof. Attention : elle ne marche que pour un heap plein, pas pour les autres OutOfMemoryError (par exemple quand la JVM ne peut plus créer de thread).

En conteneur

Sous Linux, la JVM détecte le conteneur toute seule : elle voit sa limite de mémoire et son nombre de CPU. -XX:MaxRAMPercentage donne la part de cette mémoire réservée au heap maximum, 25 % par défaut. Ce pourcentage suit la limite du conteneur, alors qu’un -Xmx fixe ne bouge pas quand la limite change. Le détail, avec des mesures : Java dans Docker et Kubernetes.

Ce qui a changé : les options qui ne marchent plus

OptionJava 8Java 11Java 17, 21 et 25À la place
-XX:PermSize, -XX:MaxPermSizeIgnorée, avec un avertissementIgnorée, avec un avertissementRefusée-XX:MetaspaceSize, -XX:MaxMetaspaceSize
-XX:+UseConcMarkSweepGCAcceptéeAcceptée, avec un avertissement (dépréciée)RefuséeUn autre GC (CMS est supprimé depuis Java 14)
-XX:+UseParNewGCAcceptéeRefuséeRefusée (supprimée en Java 10)—
-XX:+UseCGroupMemoryLimitForHeapExpérimentaleRefuséeRefusée (supprimée en Java 11)Rien : la JVM détecte le conteneur
-XX:+PrintGCDateStamps, -XX:+PrintGCTimeStampsAcceptéeRefuséeRefuséeRien : -Xlog gère les dates lui-même
-XX:+UseGCLogFileRotationAcceptéeRefuséeRefuséeRien : -Xlog gère la rotation
-XX:+PrintTenuringDistributionAcceptéeRefuséeRefusée-Xlog:gc+age*=debug
-XX:+PrintGCDetailsAcceptéeDépréciée : un avertissementDépréciée : un avertissement, puis -Xlog:gc*-Xlog:gc*
-Xloggc:<fichier>AcceptéeDépréciée : un avertissementDépréciée : un avertissement, puis -Xlog:gc:<fichier>-Xlog:gc:<fichier>

Les anciennes options de log du GC étaient donc déjà refusées en Java 11.

« Refusée » veut dire : « Unrecognized VM option », et la JVM ne démarre pas. Une option dépréciée, elle, marche encore, avec un avertissement :

[0.001s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead.

Un cas récent : -XX:+ZGenerational activait le mode générationnel de ZGC en Java 21. Depuis Java 24, ce mode est le seul : en Java 25, l’option est ignorée avec un avertissement, et -XX:+UseZGC suffit.

Tester une vieille option avant de la garder

Le plus simple est de lancer la JVM avec vos options et -version :

java -XX:+PrintGCDateStamps -Xmx2g -version

Si une option est refusée, vous le voyez tout de suite, sans lancer l’application. La documentation de la commande java a aussi trois listes : options dépréciées, obsolètes et supprimées. Mais elles sont courtes : une option supprimée il y a longtemps n’y est plus (-XX:PermSize, par exemple). Elle a aussi une table qui donne l’équivalent -Xlog de chaque ancienne option de log du GC.

Sources : la documentation de la commande java et le guide d’Oracle sur le garbage collector (JDK 25), JEP 122 (PermGen), JEP 271 (logs du GC avec -Xlog), JEP 363 (CMS), JDK-8173421 (ParNew), JDK-8194086 (cgroups), JEP 490 (ZGC).

À retenir

Gardez peu d’options, et sachez pourquoi chacune est là : la taille du heap, les logs du GC vers un fichier et le heap dump. Ne copiez pas une liste d’options d’un forum ou d’un vieux script sans la comprendre : au mieux, une partie ne sert plus à rien ; au pire, la JVM ne démarre pas.

Pour aller plus loin