Was ist Thread Dump und wie analysiert man sie?

Lassen Sie uns über den Thread-Dump sprechen und wie man ihn analysiert.

Wir werden auch besprechen, wie es hilft, die Probleme zu lokalisieren, und einige der Analysatoren, die Sie verwenden können.

Was ist Faden?

Ein Prozess ist ein Computerprogramm, das in den Speicher des Computers geladen wird und ausgeführt wird. Es kann von einem Prozessor oder einer Gruppe von Prozessoren ausgeführt werden. Ein Prozess wird im Speicher mit wichtigen Informationen wie Variablenspeichern, Dateihandles, dem Programmzähler, Registern und Signalen usw. beschrieben.

Ein Prozess kann aus vielen einfachen Prozessen bestehen, die Threads genannt werden. Dies trägt dazu bei, Parallelität zu erreichen, wobei ein Prozess in mehrere Threads unterteilt wird. Dies führt zu einer besseren Leistung. Alle Threads innerhalb eines Prozesses teilen sich denselben Speicherplatz und sind voneinander abhängig.

Thread-Dumps

Wenn der Prozess ausgeführt wird, können wir den aktuellen Ausführungsstatus der Threads im Prozess mithilfe von Thread-Dumps erkennen. Ein Thread-Dump enthält eine Momentaufnahme aller Threads, die zu einem bestimmten Zeitpunkt während der Ausführung eines Programms aktiv sind. Es enthält alle relevanten Informationen über den Thread und seinen aktuellen Status.

Eine moderne Anwendung umfasst heute mehrere Threads. Jeder Thread erfordert bestimmte Ressourcen und führt bestimmte Aktivitäten im Zusammenhang mit dem Prozess aus. Dies kann die Leistung einer Anwendung steigern, da Threads verfügbare CPU-Kerne nutzen können.

Aber es gibt Kompromisse, z. B. koordinieren manchmal mehrere Threads nicht gut miteinander und es kann zu einer Deadlock-Situation kommen. Wenn also etwas schief geht, können wir Thread-Dumps verwenden, um den Status unserer Threads zu überprüfen.

Thread-Dump in Java

Ein JVM-Thread-Dump ist eine Auflistung des Status aller Threads, die zu diesem bestimmten Zeitpunkt Teil des Prozesses sind. Es enthält Informationen über den Stack des Threads, dargestellt als Stack-Trace. Da es im Klartext geschrieben ist, kann der Inhalt zur späteren Überprüfung gespeichert werden. Die Analyse von Thread-Dumps kann dabei helfen

  • Optimieren der JVM-Leistung
  • Optimierung der Anwendungsleistung
  • Diagnostizieren von Problemen, z. B. Deadlocks, Thread-Konflikte usw.

Generierung von Thread-Dumps

Es gibt viele Möglichkeiten, Thread-Dumps zu generieren. Nachfolgend sind einige JVM-basierte Tools aufgeführt, die über die Befehlszeile/das Terminal (CLI-Tools) oder das /bin-Verzeichnis (GUI-Tools) des Installationsordners von Java ausgeführt werden können.

Lassen Sie uns sie erkunden.

#1. jStack

Der einfachste Weg, einen Thread-Dump zu generieren, ist die Verwendung von jStack. jStack wird mit JVM ausgeliefert und kann über die Befehlszeile verwendet werden. Hier benötigen wir die PID des Prozesses, für den wir den Thread-Dump erzeugen wollen. Um die PID zu erhalten, können wir den jps-Befehl wie unten gezeigt verwenden.

jps-l

jps listet alle Java-Prozess-IDs auf.

Unter Windows

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

Unter 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] ~]#

Wie wir hier sehen können, erhalten wir eine Liste aller laufenden Java-Prozesse. Es enthält die lokale VM-ID für den laufenden Java-Prozess und den Namen der Anwendung in den Spalten eins und zwei. Um nun den Thread-Dump zu generieren, verwenden wir das jStack-Programm mit dem Flag –l, das eine lang aufgelistete Ausgabe des Dumps erstellt. Wir können die Ausgabe auch an eine Textdatei unserer Wahl weiterleiten.

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 ist ein GUI-Tool, das uns bei der Fehlerbehebung, Überwachung und Profilierung von Java-Anwendungen unterstützt. Es wird auch mit JVM geliefert und kann aus dem /bin-Verzeichnis unserer Java-Installation gestartet werden. Es ist sehr intuitiv und einfach zu bedienen. Neben anderen Optionen ermöglicht es uns auch, Thread-Dump für einen bestimmten Prozess zu erfassen.

Um den Thread-Dump für einen bestimmten Prozess anzuzeigen, können wir mit der rechten Maustaste auf das Programm klicken und Thread-Dump aus dem Kontextmenü auswählen.

#3. jcmd

JCMD ist ein Befehlszeilendienstprogramm, das mit dem JDK geliefert wird und zum Senden von Diagnosebefehlsanforderungen an die JVM verwendet wird.

  So ändern Sie die Größe eines Bildes in ein Desktop-Hintergrundbild

Es funktioniert jedoch nur auf dem lokalen Rechner, auf dem die Java-Anwendung läuft. Es kann verwendet werden, um Java-Flugaufzeichnungen zu steuern, JVM- und Java-Anwendungen zu diagnostizieren und Fehler zu beheben. Wir können den Thread.print-Befehl von jcmd verwenden, um eine Liste von Thread-Dumps für einen bestimmten Prozess zu erhalten, der durch die PID angegeben ist.

Unten ist ein Beispiel dafür, wie wir jcmd verwenden können.

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 steht für Java Mission Control. Es ist ein Open-Source-GUI-Tool, das mit JDK geliefert wird und zum Sammeln und Analysieren von Java-Anwendungsdaten verwendet wird.

Es kann aus dem Ordner /bin unserer Java-Installation gestartet werden. Java-Administratoren und -Entwickler verwenden das Tool, um detaillierte Low-Level-Informationen über das Verhalten von JVMs und Anwendungen zu sammeln. Es ermöglicht eine detaillierte und effiziente Analyse der von Java Flight Recorder gesammelten Daten.

Beim Starten von jmc können wir eine Liste der Java-Prozesse sehen, die auf dem lokalen Computer ausgeführt werden. Auch eine Fernverbindung ist möglich. Bei einem bestimmten Prozess können wir mit der rechten Maustaste klicken und Start Flight Recording auswählen und dann die Thread-Dumps auf der Registerkarte Threads überprüfen.

#5. jconsole

jconsole ist ein Java Management Extension-Tool, das für das Beschwerdemanagement und die Überwachung verwendet wird.

Es verfügt auch über eine Reihe vordefinierter Operationen auf dem JMX-Agenten, die der Benutzer ausführen kann. Es ermöglicht dem Benutzer, den Stack-Trace eines Live-Programms zu erkennen und zu analysieren. Es kann aus dem Ordner /bin unserer Java-Installation gestartet werden.

Mit dem GUI-Tool jconsole können wir den Stack-Trace jedes Threads untersuchen, wenn wir ihn mit einem laufenden Java-Prozess verbinden. Dann können wir auf der Registerkarte Thread die Namen aller laufenden Threads sehen. Um einen Deadlock zu erkennen, können wir unten rechts im Fenster auf Deadlock erkennen klicken. Wenn ein Deadlock erkannt wird, wird er in einer neuen Registerkarte angezeigt, andernfalls wird kein Deadlock erkannt angezeigt.

  So sehen Sie mit Ihrem iPhone eine farbkorrigierte Welt

#6. ThreadMxBean

ThreadMXBean ist die Schnittstelle für die Verwaltung des Thread-Systems der Java Virtual Machine, die zum Paket java.lang.Management gehört. Es wird hauptsächlich verwendet, um die Threads zu erkennen, die in eine Deadlock-Situation geraten sind, und um Details darüber zu erhalten.

Wir können die ThreadMxBean-Schnittstelle verwenden, um den Thread-Dump programmgesteuert zu erfassen. Die getThreadMXBean()-Methode von ManagementFactory wird verwendet, um eine Instanz der ThreadMXBean-Schnittstelle zu erhalten. Es gibt die Anzahl sowohl von Daemon- als auch Nicht-Daemon-Live-Threads zurück. ManagementFactory ist eine Factory-Klasse zum Abrufen der verwalteten Beans für die Java-Plattform.

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 ();
}

Manuelle Analyse von Thread-Dumps

Die Analyse von Thread-Dumps kann sehr nützlich sein, um Probleme in Multithread-Prozessen zu lokalisieren. Probleme wie Deadlocks, Sperrkonflikte und übermäßige CPU-Auslastung durch einzelne Thread-Dumps können gelöst werden, indem die Zustände einzelner Thread-Dumps visualisiert werden.

Der maximale Durchsatz der Anwendung kann erreicht werden, indem der Status jedes Threads nach der Analyse des Thread-Dumps korrigiert wird.

Nehmen wir zum Beispiel an, ein Prozess verbraucht viel CPU, wir können herausfinden, ob ein Thread die CPU am meisten nutzt. Wenn es einen solchen Thread gibt, wandeln wir seine LWP-Nummer in eine Hexadezimalzahl um. Dann können wir aus dem Thread-Dump den Thread finden, dessen nid gleich der zuvor erhaltenen Hexadezimalzahl ist. Mit dem Stack-Trace des Threads können wir das Problem lokalisieren. Lassen Sie uns die Prozess-ID des Threads mit dem folgenden Befehl herausfinden.

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

Werfen wir einen Blick auf den unteren Teil des Thread-Dumps. Um einen Thread-Dump für Prozess 26680 zu erhalten, verwenden Sie 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

Lassen Sie uns nun sehen, was wir mit Thread-Dumps untersuchen können. Wenn wir den Thread-Dump beobachten, können wir viele Inhalte sehen, die überwältigend sein können. Wenn wir jedoch einen Schritt nach dem anderen machen, kann es ziemlich einfach zu verstehen sein. Lassen Sie uns die erste Zeile verstehen

2020-06-27 09:01:29
Vollständiger Thread-Dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 gemischter Modus):

Das obige zeigt die Zeit, zu der der Dump generiert wurde, und Informationen über die verwendete JVM. Als nächstes können wir am Ende die Liste der Threads sehen, der erste unter ihnen ist unser ReferenceHandler-Thread.

Blockierte Threads analysieren

Wenn wir die folgenden Thread-Dump-Protokolle analysieren, können wir feststellen, dass Threads mit BLOCKED-Status erkannt wurden, was die Leistung einer Anwendung sehr langsam macht. Wenn wir also die BLOCKED-Threads finden können, können wir versuchen, die Threads zu extrahieren, die sich auf die Sperren beziehen, die die Threads zu erhalten versuchen. Die Analyse des Stack-Trace des Threads, der derzeit die Sperre hält, kann bei der Lösung des Problems helfen.

[[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)
.
.
.
.

Deadlock-Thread analysieren

Eine weitere sehr häufig verwendete Anwendung von Thread-Dumps ist die Erkennung von Deadlocks. Die Erkennung und Lösung von Deadlocks kann viel einfacher sein, wenn wir die Thread-Dumps analysieren.

Ein Deadlock ist eine Situation, an der mindestens zwei Threads beteiligt sind, in der die von einem Thread zum Fortsetzen der Ausführung benötigte Ressource von einem anderen Thread gesperrt wird und gleichzeitig die vom zweiten Thread benötigte Ressource vom ersten Thread gesperrt wird.

  Fix Premiere Pro Beim Dekomprimieren von Audio oder Video ist ein Fehler aufgetreten

Daher kann keiner der Threads die Ausführung fortsetzen, was zu einer Deadlock-Situation führt und dazu führt, dass die Anwendung hängen bleibt. Wenn Dreadlocks vorhanden sind, gibt der letzte Abschnitt des Thread-Dumps die Informationen zum Deadlock wie folgt aus.

"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)

Hier sehen wir die Deadlock-Informationen in einem für Menschen lesbaren Format.

Abgesehen davon, wenn wir den gesamten obigen Teil des Thread-Dumps zusammenfassen, werden die folgenden Informationen angegeben.

  • Referenzhandler ist der für Menschen lesbare Name des Threads.
  • #2 ist die eindeutige ID des Threads.
  • daemon gibt an, ob der Thread ein Daemon-Thread ist.
  • Die numerische Priorität des Threads wird durch prio=10 angegeben.
  • Der aktuelle Status des Threads wird durch Warten auf Bedingung gekennzeichnet.
  • Dann sehen wir den Stack-Trace, der die Sperrinformationen enthält.

Thread-Dump-Analysatoren

Neben der manuellen Analyse stehen zahlreiche Tools zur Verfügung, um Thread-Dumps sowohl online als auch offline zu analysieren. Nachfolgend finden Sie einige der aufgeführten Tools, die wir je nach Anforderung einsetzen können.

Sehen wir uns zunächst Online-Tools an.

#1. Schneller Faden

Schneller Faden ist das beliebteste Tool für die Thread-Dump-Analyse von DevOps-Ingenieuren, um komplexe Produktionsprobleme zu beheben. Dies ist ein Online-Java-Thread-Dump-Analysator. Wir können den Thread-Dump als Datei hochladen oder den Thread-Dump direkt kopieren und einfügen.

Abhängig von der Größe analysiert es den Thread-Dump und zeigt die Informationen wie im Screenshot gezeigt an.

Merkmale

  • Beheben Sie JVM-Abstürze, Verlangsamungen, Speicherlecks, Einfrieren und CPU-Spitzen
  • Instant RCA (warte nicht auf Anbieter)
  • Intuitives Dashboard
  • REST-API-Unterstützung
  • Maschinelles Lernen

#2. Spotify-Thread-Dump-Analysator

Das Spotify-Thread-Dump-Analysator ist unter Version 2.0 der Apache-Lizenz lizenziert. Es ist ein Online-Tool und akzeptiert den Thread-Dump als Datei, oder wir können den Thread-Dump direkt kopieren und einfügen. Abhängig von der Größe analysiert es den Thread-Dump und zeigt die Informationen wie im Screenshot gezeigt an.

#3. Jstack-Überprüfung

Jstack.review analysiert Java-Thread-Dumps innerhalb des Browsers. Diese Seite ist nur die Client-Seite.

#4. Seite 24×7

Dies Werkzeug ist eine Voraussetzung für die Erkennung fehlerhafter Threads, die die Leistung der Java Virtual Machine (JVM) beeinträchtigen. Probleme wie Deadlocks, Sperrkonflikte und übermäßige CPU-Auslastung durch einzelne Thread-Dumps können gelöst werden, indem die Zustände einzelner Thread-Dumps visualisiert werden.

Der maximale Durchsatz der App kann erreicht werden, indem der Status jedes vom Tool bereitgestellten Threads korrigiert wird.

Sehen wir uns nun die Offline-Tools an.

Beim Profiling ist nur das beste Tool gut genug.

#1. JProfiler

JProfiler ist einer der beliebtesten Thread-Dump-Analysatoren unter Java-Entwicklern. Die intuitive Benutzeroberfläche von JProfiler hilft Ihnen, Leistungsengpässe zu beheben, Speicherlecks aufzuspüren und Threading-Probleme zu verstehen.

JProfiler unterstützt die Profilerstellung auf den folgenden Plattformen:

  • Fenster
  • Mac OS
  • Linux
  • FreeBSD
  • Solaris
  • AIX
  • HP-UX

Nachfolgend sind einige Funktionen aufgeführt, die JProfiler zur ersten Wahl für die Profilerstellung unserer Anwendungen auf der JVM machen.

Merkmale

  • Unterstützt Datenbank-Profiling für JDBC, JPA und NoSQL
  • Unterstützung für Java Enterprise Edition ist ebenfalls verfügbar
  • Präsentiert allgemeine Informationen zu RMI-Anrufen
  • Hervorragende Analyse von Speicherlecks
  • Umfangreiche QA-Funktionen
  • Der integrierte Thread-Profiler ist eng mit den CPU-Profiling-Ansichten integriert.
  • Unterstützung für Plattformen, IDEs und Anwendungsserver.

#2. IBM TMDA

IBM Thread and Monitor Dump Analyzer for Java (TMDA) ist ein Tool, mit dem Hänge, Deadlocks, Ressourcenkonflikte und Engpässe in Java-Thread-Dumps identifiziert werden können. Es ist ein IBM-Produkt, aber das TMDA-Tool wird ohne Gewährleistung oder Support bereitgestellt; Sie versuchen jedoch, das Tool im Laufe der Zeit zu reparieren und zu verbessern.

#3. ManageEngine

ManageEngine Der Anwendungsmanager kann helfen, JVM-Heap- und Nicht-Heap-Speicher zu überwachen. Wir können sogar Schwellenwerte konfigurieren und per E-Mail, SMS usw. benachrichtigt werden und sicherstellen, dass eine Java-Anwendung gut abgestimmt ist.

#4. DeinKit

DeinKit besteht aus den folgenden Produkten, die als Kit bezeichnet werden.

  • Java Profiler – Voll ausgestatteter Low-Overhead-Profiler für Java EE- und Java SE-Plattformen.
  • YouMonitor – Leistungsüberwachung und Profilerstellung von Jenkins, TeamCity, Gradle, Maven, Ant, JUnit und TestNG.
  • .NET Profiler – Benutzerfreundlicher Leistungs- und Speicherprofiler für .NET Framework.

Fazit

Jetzt wissen Sie, wie Thread-Dumps nützlich sind, um Probleme in Multithread-Anwendungen zu verstehen und zu diagnostizieren. Mit richtig Wissenin Bezug auf die Thread-Dumps – ihre Struktur, die darin enthaltenen Informationen usw. – können wir sie verwenden, um die Ursachen der Probleme schnell zu identifizieren.