Java
Service Java lent ou figé : combiner thread dumps et JFR
Choisir entre thread dump, captures répétées et fenêtre JFR puis croiser les indices pour guider le diagnostic.
Une requête reste bloquée, le CPU grimpe ou la latence se dégrade pendant plusieurs minutes. La collecte utile dépend de la durée du symptôme et de la question à trancher. Un thread dump montre un instant; plusieurs dumps révèlent ce qui persiste; JFR donne une fenêtre d’événements. On peut commencer par l’un et compléter avec l’autre.
Choisir la collecte selon le symptôme
| Symptôme | Question immédiate | Thread dump | JFR | Stratégie utile |
|---|---|---|---|---|
| Application figée maintenant | Où les threads attendent-ils ? | Un dump donne les piles, états et verrous visibles à cet instant. | Une fenêtre courte peut révéler l’activité récente, si elle a été enregistrée. | Capturer d’abord un dump; démarrer JFR si le dump ne distingue pas attente durable et activité transitoire. |
| Latence qui dure | Une pile ou une contention revient-elle ? | Plusieurs dumps espacés montrent les piles et états persistants. | Les événements et échantillons replacent l’activité dans le temps. | Faire une série de dumps et une fenêtre JFR qui couvre le même intervalle. |
| CPU élevé | Quels threads consomment le CPU et que font-ils ? | Des piles répétées aident à localiser les threads actifs, sans mesurer leur consommation sur toute la période. | Les échantillons d’exécution aident à comparer l’activité au cours de la fenêtre. | Utiliser JFR comme vue temporelle; ajouter des dumps si une pile précise doit être examinée. |
| Blocage intermittent ou contention | Le même verrou ou la même attente revient-il ? | Plusieurs captures peuvent montrer l’attente et les propriétaires de verrous visibles. | JFR peut apporter la chronologie d’événements de contention configurés. | Collecter les deux pendant le symptôme si celui-ci dure assez longtemps. |
| Dégradation passée ou intermittente | Que s’est-il passé pendant la période ? | Un dump pris après coup ne reconstitue pas l’état précédent. | Un enregistrement déjà actif peut conserver des événements de la période. | Préparer une fenêtre JFR bornée; prendre un dump si le symptôme se reproduit. |
Le dump apporte une photographie des threads. Une série aide à distinguer une attente stable d’un instant passager. JFR apporte une chronologie, selon les événements activés et les données collectées. Croisez ces observations avec le code, les journaux et le contexte de la requête.
Commencer simple puis compléter
Pour un gel en cours, commencez par un thread dump et notez l’heure ainsi que le symptôme. Si plusieurs threads attendent sur le même verrou ou si la même pile revient, capturez à nouveau après quelques secondes. Si les états changent rapidement ou si la latence dépend d’une période plus longue, ajoutez une fenêtre JFR courte. Rapprochez les horodatages pour vérifier si les indices concordent.
Pour le CPU, JFR est souvent plus utile pour comparer l’activité dans le temps. Un dump pris pendant le pic peut compléter l’enregistrement en montrant les piles à cet instant. Pour suivre un incident intermittent, démarrez JFR avant l’occurrence et capturez des dumps pendant le symptôme.
Préparer l’accès à la JVM
Les exemples ciblent HotSpot avec les outils du JDK 25. Les commandes exposées dépendent de la JVM et de sa configuration. Vérifiez que jcmd et jfr sont disponibles, que l’outil tourne sur la même machine et sous les mêmes identifiants effectifs que la JVM. Choisissez aussi un emplacement protégé où les fichiers peuvent être écrits.
Listez les JVM visibles puis demandez à la cible ses commandes disponibles :
jcmd -l
jcmd <pid> help
jcmd <pid> help Thread.print
jcmd <pid> help JFR.start
Remplacez <pid> par l’identifiant relevé dans la première commande. Ne supposez pas qu’une commande existe avant d’avoir interrogé la JVM concernée.
Capturer et comparer des threads
Oracle SE 25 classe l’impact de Thread.print comme moyen selon le nombre de threads. En incident, espacez les captures et adaptez leur fréquence à la JVM : la collecte a un coût. Une capture ponctuelle s’effectue ainsi :
jcmd <pid> Thread.print -l > thread-1.txt
-l demande les informations sur les verrous java.util.concurrent, lorsque cette option est prise en charge. Pour comparer l’évolution, associez l’heure de début à chaque fichier plutôt que d’imprimer l’heure à part dans le terminal :
pid=12345 # remplacer par le PID repéré avec jcmd -l
diagnostics_dir=$(mktemp -d /tmp/java-diag.XXXXXX)
for n in 1 2 3; do
file="$diagnostics_dir/thread-$n.txt"
if {
printf '# Capture commencée à %s\n' "$(date)"
jcmd "$pid" Thread.print -l
} > "$file" 2>&1; then
:
else
status=$?
printf '# Échec de jcmd, code %s; voir le diagnostic ci-dessus.\n' "$status" >> "$file"
fi
sleep 10
done
Chaque fichier contient son horodatage, la sortie de jcmd et, en cas d’échec, son diagnostic et code retour. Dans les captures réussies, rapprochez le symptôme des états, piles et verrous visibles puis comparez les mêmes threads. Une pile répétée oriente l’enquête vers une attente persistante ou une activité longue; croisez-la avec le code et le contexte de la requête.
Enregistrer une fenêtre JFR
Si la JVM expose les commandes JFR, vous pouvez démarrer un enregistrement borné puis examiner le fichier produit :
pid=12345 # remplacer par le PID de la JVM
diagnostics_dir=$(mktemp -d /tmp/jfr-triage.XXXXXX)
recording="$diagnostics_dir/triage-${pid}.jfr"
if jcmd "$pid" JFR.start name=triage settings=profile duration=60s filename="$recording"; then
sleep 65
if [ -s "$recording" ]; then
jfr summary "$recording"
else
printf 'Capture absente ou vide : examiner le diagnostic JVM.\n' >&2
fi
else
printf 'Démarrage JFR refusé : examiner le diagnostic avant de continuer.\n' >&2
fi
Vérifiez au préalable l’aide JFR.start de la cible. mktemp -d crée ici un répertoire temporaire privé et absolu; choisissez un emplacement auquel le processus JVM peut écrire et que jfr peut lire. recording est transmis tel quel à jcmd puis à jfr summary. Protégez ce fichier de diagnostic. L’enregistrement dure 60 secondes; attendez sa fin avant de résumer le fichier. Le profil profile collecte plus de données que default.jfc et peut avoir davantage d’impact; gardez cette fenêtre courte et consultez la documentation du JDK ciblé. Rapprochez la chronologie d’événements du blocage observé et des autres indices.
Les captures peuvent contenir des noms de threads, des piles, des chemins et d’autres détails applicatifs. Limitez leur accès, évitez de les joindre telles quelles à un ticket public et expurgez tout extrait partagé.
Sources techniques
- The jcmd CommandOracle
- Troubleshooting Guide, Java SE 25Oracle
- The jfr CommandOracle