Qu’est-ce que le Thread Dump et comment les analyser ?

Parlons du vidage de thread et de la manière de l’analyser.

Nous discuterons également de la manière dont cela aide à identifier les problèmes et certains des analyseurs que vous pouvez utiliser.

Qu’est-ce que le fil ?

Un processus est un programme informatique chargé dans la mémoire de l’ordinateur et en cours d’exécution. Il peut être exécuté par un processeur ou un ensemble de processeurs. Un processus est décrit en mémoire avec des informations importantes telles que les magasins de variables, les descripteurs de fichiers, le compteur de programme, les registres et les signaux, etc.

Un processus peut être constitué de plusieurs processus légers appelés threads. Cela permet d’obtenir un parallélisme dans lequel un processus est divisé en plusieurs threads. Cela se traduit par de meilleures performances. Tous les threads d’un processus partagent le même espace mémoire et dépendent les uns des autres.

Vidages de threads

Lorsque le processus est en cours d’exécution, nous pouvons détecter l’état actuel d’exécution des threads dans le processus à l’aide de thread dumps. Un vidage de thread contient un instantané de tous les threads actifs à un moment donné de l’exécution d’un programme. Il contient toutes les informations pertinentes sur le thread et son état actuel.

Une application moderne implique aujourd’hui plusieurs nombres de threads. Chaque thread nécessite certaines ressources, effectue certaines activités liées au processus. Cela peut améliorer les performances d’une application car les threads peuvent utiliser les cœurs de processeur disponibles.

Mais il y a des compromis, par exemple, parfois plusieurs threads peuvent ne pas bien se coordonner les uns avec les autres et une situation de blocage peut survenir. Donc, si quelque chose ne va pas, nous pouvons utiliser des vidages de threads pour inspecter l’état de nos threads.

Vidage de thread en Java

Un vidage de thread JVM est une liste de l’état de tous les threads qui font partie du processus à ce moment précis. Il contient des informations sur la pile du thread, présentées sous la forme d’une trace de pile. Comme il est écrit en clair, le contenu peut être enregistré pour une révision ultérieure. L’analyse des vidages de threads peut aider à

  • Optimisation des performances JVM
  • Optimisation des performances des applications
  • Diagnostiquer des problèmes, par exemple un blocage, un conflit de threads, etc.

Génération de thread dumps

Il existe de nombreuses façons de générer des vidages de thread. Vous trouverez ci-dessous quelques outils basés sur JVM et pouvant être exécutés à partir de la ligne de commande/du terminal (outils CLI) ou du répertoire /bin (outils GUI) du dossier d’installation de Java.

Explorons-les.

#1. jStackName

Le moyen le plus simple de générer un vidage de thread consiste à utiliser jStack. jStack est livré avec JVM et peut être utilisé à partir de la ligne de commande. Ici, nous avons besoin du PID du processus pour lequel nous voulons générer le thread dump. Pour obtenir le PID, nous pouvons utiliser la commande jps comme indiqué ci-dessous.

jps-l

jps répertorie tous les identifiants de processus Java.

Sous Windows

C:Program FilesJavajdk1.8.0_171bin>jps -l
47172 portal
6120 sun.tools.jps.Jps
C:Program FilesJavajdk1.8.0_171bin>

Sous Linux

[[email protected] ~]# jps -l
1088 /opt/keycloak/jboss-modules.jar
26680 /var/lib/jenkins/workspace/kyc/kyc/target/kyc-1.0.jar
7193 jdk.jcmd/sun.tools.jps.Jps
2058 /usr/share/jenkins/jenkins.war
11933 /var/lib/jenkins/workspace/admin-portal/target/portal-1.0.jar
[[email protected] ~]#

Comme nous pouvons le voir ici, nous obtenons une liste de tous les processus Java en cours d’exécution. Il contient l’identifiant de la machine virtuelle locale pour le processus Java en cours d’exécution et le nom de l’application dans les colonnes un et deux respectivement. Maintenant, pour générer le vidage de thread, nous utilisons le programme jStack avec l’indicateur –l qui crée une longue liste de sortie du vidage. Nous pouvons également diriger la sortie vers un fichier texte de notre choix.

jstack-l 26680

[[email protected] ~]# jstack -l 26680
2020-06-27 06:04:53
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"Attach Listener" #16287 daemon prio=9 os_prio=0 tid=0x00007f0814001800 nid=0x4ff2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"logback-8" #2316 daemon prio=5 os_prio=0 tid=0x00007f07e0033000 nid=0x4792 waiting on condition [0x00007f07baff8000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006ca9a1fc0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"logback-7" #2315 daemon prio=5 os_prio=0 tid=0x00007f07e0251800 nid=0x4791 waiting on condition [0x00007f07bb0f9000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006ca9a1fc0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

#2. jvisualvm

Jvisualvm est un outil graphique qui nous aide à dépanner, surveiller et profiler les applications Java. Il est également livré avec JVM et peut être lancé à partir du répertoire /bin de notre installation java. Il est très intuitif et facile à utiliser. Entre autres options, cela nous permet également de capturer le vidage de thread pour un processus particulier.

Pour afficher le vidage de thread pour un processus particulier, nous pouvons cliquer avec le bouton droit sur le programme et sélectionner Thread Dump dans le menu contextuel.

#3. jcmd

JCMD est un utilitaire de ligne de commande fourni avec le JDK et utilisé pour envoyer des demandes de commande de diagnostic à la JVM.

Cependant, cela ne fonctionne que sur la machine locale sur laquelle l’application Java est en cours d’exécution. Il peut être utilisé pour contrôler les enregistrements de vol Java, diagnostiquer et dépanner les applications JVM et Java. Nous pouvons utiliser la commande Thread.print de jcmd pour obtenir une liste de vidages de threads pour un processus particulier spécifié par le PID.

Vous trouverez ci-dessous un exemple d’utilisation de jcmd.

jcmd 28036 Thread.print

C:Program FilesJavajdk1.8.0_171bin>jcmd 28036 Thread.print
28036:
2020-06-27 21:20:02
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):

"Bundle File Closer" #14 daemon prio=5 os_prio=0 tid=0x0000000021d1c000 nid=0x1d4c in Object.wait() [0x00000000244ef000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Unknown Source)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:403)
        - locked <0x000000076f380a88> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

"Active Thread: Equinox Container: 0b6cc851-96cd-46de-a92b-253c7f7671b9" #12 prio=5 os_prio=0 tid=0x0000000022e61800 nid=0xbff4 waiting on condition [0x00000000243ee000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000076f388188> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(Unknown Source)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000021a7b000 nid=0x2184 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x00000000219f5000 nid=0x1300 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x00000000219e0000 nid=0x48f4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x00000000219df000 nid=0xb314 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x00000000219db800 nid=0x2260 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000219d9000 nid=0x125c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000219d8000 nid=0x834 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001faf3000 nid=0x36c0 in Object.wait() [0x0000000021eae000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076f390180> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        - locked <0x000000076f390180> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000005806000 nid=0x13c0 in Object.wait() [0x00000000219af000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076f398178> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Unknown Source)
        at java.lang.ref.Reference.tryHandlePending(Unknown Source)
        - locked <0x000000076f398178> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

"main" #1 prio=5 os_prio=0 tid=0x000000000570e800 nid=0xbf8 runnable [0x0000000000fec000]
   java.lang.Thread.State: RUNNABLE
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.<init>(Unknown Source)
        at java.util.zip.ZipFile.<init>(Unknown Source)
        at java.util.zip.ZipFile.<init>(Unknown Source)
        at org.eclipse.osgi.framework.util.SecureAction.getZipFile(SecureAction.java:307)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.getZipFile(ZipBundleFile.java:136)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.lockOpen(ZipBundleFile.java:83)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.getEntry(ZipBundleFile.java:290)
        at org.eclipse.equinox.weaving.hooks.WeavingBundleFile.getEntry(WeavingBundleFile.java:65)
        at org.eclipse.osgi.storage.bundlefile.BundleFileWrapper.getEntry(BundleFileWrapper.java:55)
        at org.eclipse.osgi.storage.BundleInfo$Generation.getRawHeaders(BundleInfo.java:130)
        - locked <0x000000076f85e348> (a java.lang.Object)
        at org.eclipse.osgi.storage.BundleInfo$CachedManifest.get(BundleInfo.java:599)
        at org.eclipse.osgi.storage.BundleInfo$CachedManifest.get(BundleInfo.java:1)
        at org.eclipse.equinox.weaving.hooks.SupplementerRegistry.addSupplementer(SupplementerRegistry.java:172)
        at org.eclipse.equinox.weaving.hooks.WeavingHook.initialize(WeavingHook.java:138)
        at org.eclipse.equinox.weaving.hooks.WeavingHook.start(WeavingHook.java:208)
        at org.eclipse.osgi.storage.FrameworkExtensionInstaller.startActivator(FrameworkExtensionInstaller.java:261)
        at org.eclipse.osgi.storage.FrameworkExtensionInstaller.startExtensionActivators(FrameworkExtensionInstaller.java:198)
        at org.eclipse.osgi.internal.framework.SystemBundleActivator.start(SystemBundleActivator.java:112)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:815)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:808)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:765)
        at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1005)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.initWorker(EquinoxBundle.java:190)
        at org.eclipse.osgi.container.SystemModule.init(SystemModule.java:99)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle.init(EquinoxBundle.java:272)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle.init(EquinoxBundle.java:257)
        at org.eclipse.osgi.launch.Equinox.init(Equinox.java:171)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.startup(EclipseStarter.java:316)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:251)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:661)
        at org.eclipse.equinox.launcher.Main.basicRun(Main.java:597)
        at org.eclipse.equinox.launcher.Main.run(Main.java:1476)

"VM Thread" os_prio=2 tid=0x000000001fae8800 nid=0x32cc runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000005727800 nid=0x3264 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000005729000 nid=0xbdf4 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000572a800 nid=0xae6c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000572d000 nid=0x588 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000000000572f000 nid=0xac0 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000005730800 nid=0x380 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000005733800 nid=0x216c runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000005734800 nid=0xb930 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000021a8d000 nid=0x2dcc waiting on condition

JNI global references: 14


C:Program FilesJavajdk1.8.0_171bin>

#4. JMC

JMC signifie Java Mission Control. Il s’agit d’un outil graphique open source fourni avec JDK et utilisé pour collecter et analyser les données d’application Java.

Il peut être lancé depuis le dossier /bin de notre installation Java. Les administrateurs et les développeurs Java utilisent l’outil pour collecter des informations détaillées de bas niveau sur les comportements de la JVM et de l’application. Il permet une analyse détaillée et efficace des données collectées par Java Flight Recorder.

Au lancement de jmc, nous pouvons voir la liste des processus java qui s’exécutent sur la machine locale. Une connexion à distance est également possible. Sur un processus particulier, nous pouvons cliquer avec le bouton droit de la souris et choisir Démarrer l’enregistrement de vol, puis vérifier les vidages de threads dans l’onglet Threads.

#5. jconsole

jconsole est un outil Java Management Extension utilisé pour la gestion et la surveillance des plaintes.

Il dispose également d’un ensemble d’opérations prédéfinies sur l’agent JMX que l’utilisateur peut effectuer. Il permet à l’utilisateur de détecter et d’analyser la trace de la pile d’un programme en direct. Il peut être lancé depuis le dossier /bin de notre installation Java.

À l’aide de l’outil graphique jconsole, nous pouvons inspecter la trace de la pile de chaque thread lorsque nous le connectons à un processus Java en cours d’exécution. Ensuite, dans l’onglet Thread, nous pouvons voir le nom de tous les threads en cours d’exécution. Pour détecter un interblocage, nous pouvons cliquer sur le bouton Détecter l’interblocage en bas à droite de la fenêtre. Si un interblocage est détecté, il apparaîtra dans un nouvel onglet sinon un No Deadlock Detected sera affiché.

#6. ThreadMxBean

ThreadMXBean est l’interface de gestion du système de threads de la machine virtuelle Java appartenant au package java.lang.Management. Il est principalement utilisé pour détecter les threads qui sont entrés dans une situation de blocage et obtenir des détails à leur sujet.

Nous pouvons utiliser l’interface ThreadMxBean pour capturer par programmation le vidage de thread. La méthode getThreadMXBean() de ManagementFactory est utilisée pour obtenir une instance de l’interface ThreadMXBean. Il renvoie le nombre de threads en direct démons et non démons. ManagementFactory est une classe de fabrique permettant d’obtenir les beans gérés pour la plate-forme Java.

private static String getThreadDump (boolean lockMonitors, boolean lockSynchronizers) {
    StringBuffer threadDump = new StringBuffer (System.lineSeparator ());
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean ();
    for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads (lockMonitors, lockSynchronizers)) {
        threadDump.append (threadInfo.toString ());
    }
    return threadDump.toString ();
}

Analyse manuelle des vidages de threads

L’analyse des vidages de threads peut être très utile pour identifier les problèmes dans les processus multithreads. Les problèmes tels que les interblocages, les conflits de verrouillage et l’utilisation excessive du processeur par les vidages de threads individuels peuvent être résolus en visualisant les états des vidages de threads individuels.

Le débit maximal de l’application peut être atteint en rectifiant l’état de chaque thread après avoir analysé le vidage de thread.

Par exemple, disons qu’un processus utilise beaucoup de CPU, nous pouvons savoir si un thread utilise le plus le CPU. S’il existe un tel thread, nous convertissons son numéro LWP en un nombre hexadécimal. Ensuite, à partir du thread dump, nous pouvons trouver le thread avec nid égal au nombre hexadécimal obtenu précédemment. En utilisant la trace de pile du thread, nous pouvons identifier le problème. Découvrons l’ID de processus du thread à l’aide de la commande ci-dessous.

ps -mo pid,lwp,stime,time,cpu -C java

[[email protected] ~]# ps -mo pid,lwp,stime,time,cpu -C java
       PID        LWP         STIME           TIME              %CPU
26680               -         Dec07          00:02:02           99.5
         -       10039        Dec07          00:00:00           0.1
         -       10040        Dec07          00:00:00           95.5

Jetons un coup d’œil au morceau ci-dessous de vidage de thread. Pour obtenir un vidage de thread pour le processus 26680, utilisez jstack -l 26680

[[email protected] ~]# jstack -l 26680
2020-06-27 09:01:29
<strong>Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):</strong>

"Attach Listener" #16287 daemon prio=9 os_prio=0 tid=0x00007f0814001800 nid=0x4ff2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

.
.
.
.
.
.
.
"<strong>Reference Handler</strong>" #2 daemon prio=10 os_prio=0 tid=0x00007f085814a000 nid=0x6840 in Object.wait() [0x00007f083b2f1000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000006c790fbd0> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=0 tid=0x00007f0858140800 nid=0x683f runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f0858021000 nid=0x683b runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f0858022800 nid=0x683c runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f0858024800 nid=0x683d runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f0858026000 nid=0x683e runnable

"VM Periodic Task Thread" os_prio=0 tid=0x00007f08581a0000 nid=0x6847 waiting on condition

JNI global references: 1553

Voyons maintenant quelles sont les choses que nous pouvons explorer en utilisant les thread dumps. Si nous observons le thread dump, nous pouvons voir beaucoup de contenu, ce qui peut être écrasant. Cependant, si nous prenons une étape à la fois, cela peut être assez simple à comprendre. Comprenons la première ligne

2020-06-27 09:01:29
Vidage complet du thread Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mode mixte) :

Ce qui précède affiche l’heure à laquelle le vidage a été généré et des informations sur la JVM utilisée. Ensuite, à la fin, nous pouvons voir la liste des threads, le premier d’entre eux est notre thread ReferenceHandler.

Analyser les threads bloqués

Si nous analysons les journaux de vidage de thread ci-dessous, nous pouvons constater qu’il a détecté des threads avec l’état BLOQUÉ, ce qui ralentit considérablement les performances d’une application. Donc, si nous pouvons trouver les threads BLOQUÉS, nous pouvons essayer d’extraire les threads liés aux verrous que les threads tentent d’obtenir. L’analyse de la trace de la pile du thread qui détient actuellement le verrou peut aider à résoudre le problème.

[[email protected] ~]# jstack -l 26680
.
.
.
.
" DB-Processor-13" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f000]
java.lang.Thread.State: <strong>BLOCKED</strong> (on object monitor)
                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
                - waiting to lock <0xe0375410> (a beans.ConnectionPool)
                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)
                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)
"DB-Processor-14" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f020]
java.lang.Thread.State: <strong>BLOCKED</strong> (on object monitor)
                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
                - waiting to lock <0xe0375410> (a beans.ConnectionPool)
                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)
                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)
.
.
.
.

Analyse d’un thread bloqué

Une autre application très couramment utilisée des vidages de threads est la détection des blocages. La détection et la résolution des blocages peuvent être beaucoup plus faciles si nous analysons les thread dumps.

Un blocage est une situation impliquant au moins deux threads où la ressource requise par un thread pour continuer l’exécution est verrouillée par un autre thread et en même temps, la ressource requise par le deuxième thread est verrouillée par le premier thread.

Ainsi, aucun des threads ne peut continuer à s’exécuter, ce qui entraîne une situation de blocage et aboutit au blocage de l’application. Si des dreadlocks sont présents, la dernière section du vidage de thread imprimera les informations concernant le blocage comme suit.

"Thread-0":
waiting to lock monitor 0x00000250e4982480 (object 0x00000000894465b0, a java.lang.Object),
which is held by "Thread-1"
"Thread-1":
waiting to lock monitor 0x00000250e4982380 (object 0x00000000894465a0, a java.lang.Object),
which is held by "Thread-0"
.
.
.
"Thread-0":
at DeadlockedProgram$DeadlockedRunnableImplementation.run(DeadlockedProgram.java:34)
- waiting to lock <0x00000000894465b0> (a java.lang.Object)
- locked <0x00000000894465a0> (a java.lang.Object)
at java.lang.Thread.run([email protected]/Thread.java:844)
"Thread-1":
at DeadlockedProgram $DeadlockRunnableImplementation.run(DeadlockedProgram.java:34)
- waiting to lock <0x00000000894465a0> (a java.lang.Object)
- locked <0x00000000894465b0> (a java.lang.Object)
at java.lang.Thread.run([email protected]/Thread.java:844)

Ici, nous pouvons voir les informations de blocage dans un format assez lisible par l’homme.

En dehors de cela, si nous résumons ensemble tout le morceau de vidage de thread ci-dessus, il indique les informations ci-dessous.

  • Le gestionnaire de référence est le nom lisible par l’homme du thread.
  • #2 est l’identifiant unique du thread.
  • daemon indique si le thread est un thread démon.
  • La priorité numérique du thread est donnée par prio=10.
  • L’état actuel du thread est indiqué par une condition d’attente.
  • Ensuite, nous voyons la trace de la pile, qui inclut les informations de verrouillage.

Analyseurs de vidages de threads

Outre l’analyse manuelle, de nombreux outils sont disponibles pour analyser les vidages de threads, à la fois en ligne et hors ligne. Vous trouverez ci-dessous certains des outils répertoriés, que nous pouvons utiliser en fonction des besoins.

Tout d’abord, explorons les outils en ligne.

#1. Filetage rapide

Filetage rapide est l’outil d’analyse de thread dump préféré des ingénieurs DevOps pour résoudre les problèmes de production complexes. Il s’agit d’un analyseur de vidage de thread Java en ligne. Nous pouvons télécharger le vidage de thread sous forme de fichier ou nous pouvons directement copier et coller le vidage de thread.

Selon la taille, il analysera le thread dump et affichera les informations comme indiqué dans la capture d’écran.

Fonctionnalités

  • Résoudre les problèmes de plantages, de ralentissements, de fuites de mémoire, de gels et de pics de processeur JVM
  • RCA instantané (n’attendez pas les vendeurs)
  • Tableau de bord intuitif
  • Prise en charge de l’API REST
  • Apprentissage automatique

#2. Analyseur de vidage de thread Spotify

La Analyseur de vidage de thread Spotify est sous licence sous la version 2.0 de la licence Apache. C’est un outil en ligne et accepte le vidage de thread sous forme de fichier ou nous pouvons directement copier et coller le vidage de thread. Selon la taille, il analysera le thread dump et affichera les informations comme indiqué dans la capture d’écran.

#3. Revue Jstack

Jstack.revue analyse les vidages de threads Java à partir du navigateur. Cette page est uniquement côté client.

#4. Site 24×7

Cette outil est une condition préalable à la détection des threads défectueux dégradant les performances de la machine virtuelle Java (JVM). Les problèmes tels que les interblocages, les conflits de verrouillage et l’utilisation excessive du processeur par les vidages de threads individuels peuvent être résolus en visualisant les états des vidages de threads individuels.

Le débit maximal de l’application peut être atteint en rectifiant l’état de chaque thread fourni par l’outil.

Maintenant, explorons les outils hors ligne.

En matière de profilage, seul le meilleur outil suffit.

#1. JProfilerName

JProfilerName est l’un des analyseurs de vidage de thread les plus populaires parmi les développeurs Java. L’interface utilisateur intuitive de JProfiler vous aide à résoudre les goulots d’étranglement des performances, à identifier les fuites de mémoire et à comprendre les problèmes de threading.

JProfiler prend en charge le profilage sur les plates-formes suivantes :

  • les fenêtres
  • macOS
  • Linux
  • FreeBSD
  • Solaris
  • AIX
  • HP-UX

Vous trouverez ci-dessous quelques fonctionnalités qui font de JProfiler le meilleur choix pour profiler nos applications sur la JVM.

Fonctionnalités

  • Prend en charge le profilage de base de données pour JDBC, JPA et NoSQL
  • La prise en charge de Java Enterprise Edition est également disponible
  • Présente des informations de haut niveau sur les appels RMI
  • Analyse stellaire des fuites de mémoire
  • Capacités d’assurance qualité étendues
  • Le profileur de threads intégré est étroitement intégré aux vues de profilage du processeur.
  • Prise en charge des plates-formes, des IDE et des serveurs d’applications.

#2. IBM TMDA

IBM Thread and Monitor Dump Analyzer pour Java (TMDA) est un outil qui permet d’identifier les blocages, les blocages, les conflits de ressources et les goulots d’étranglement dans les vidages de threads Java. Il s’agit d’un produit IBM mais l’outil TMDA est fourni sans aucune garantie ni assistance ; cependant, ils essaient de réparer et d’améliorer l’outil au fil du temps.

#3. Gérer le moteur

Gérer le moteur le gestionnaire d’applications peut aider à surveiller la mémoire JVM Heap et Non-Heap. Nous pouvons même configurer des seuils et être alertés par e-mail, SMS, etc., et nous assurer qu’une application Java est bien réglée.

#4. VotreKit

VotreKit se compose des produits ci-dessous appelés en tant que kit.

  • Java Profiler – Profileur complet à faible surcharge pour les plates-formes Java EE et Java SE.
  • YouMonitor – Surveillance des performances et profilage de Jenkins, TeamCity, Gradle, Maven, Ant, JUnit et TestNG.
  • .NET Profiler – Profileur de performances et de mémoire facile à utiliser pour le framework .NET.

Conclusion

Vous savez maintenant à quel point les vidages de threads sont utiles pour comprendre et diagnostiquer les problèmes dans les applications multithread. Avec un bon connaissancesen ce qui concerne les vidages de threads – leur structure, les informations qu’ils contiennent, etc. – nous pouvons les utiliser pour identifier rapidement les causes des problèmes.