03/19/2024

Ce este Thread Dump și cum să le analizezi?

Să vorbim despre descărcarea firului și cum să o analizăm.

Vom discuta, de asemenea, cum vă ajută să identificați problemele și unele dintre analizoarele pe care le puteți utiliza.

Ce este Thread?

Un proces este un program de calculator care este încărcat în memoria computerului și este în curs de execuție. Poate fi executat de un procesor sau de un set de procesoare. Un proces este descris în memorie cu informații importante, cum ar fi depozite de variabile, mânere de fișiere, contor de program, registre și semnale și așa mai departe.

Un proces poate consta din multe procese ușoare numite fire. Acest lucru ajută la obținerea paralelismului în care un proces este împărțit în mai multe fire. Acest lucru are ca rezultat o performanță mai bună. Toate firele dintr-un proces partajează același spațiu de memorie și depind unele de altele.

Dumps de fire

Când procesul este în execuție, putem detecta starea curentă de execuție a firelor de execuție din proces utilizând dump-uri de fire. Un thread Dump conține un instantaneu al tuturor thread-urilor active la un anumit punct în timpul execuției unui program. Conține toate informațiile relevante despre fir și despre starea sa actuală.

O aplicație modernă de astăzi implică mai multe numere de fire. Fiecare fir necesită anumite resurse, realizează anumite activități legate de proces. Acest lucru poate crește performanța unei aplicații, deoarece firele de execuție pot utiliza nucleele CPU disponibile.

Dar există compromisuri, de exemplu, uneori mai multe fire de execuție pot să nu se coordoneze bine între ele și poate apărea o situație de blocaj. Așadar, dacă ceva nu merge bine, putem folosi file-uri pentru a inspecta starea firelor noastre.

Dump de fire în Java

Un fir de execuție JVM Dump este o listă a stării tuturor firelor de execuție care fac parte din proces în acel moment particular de timp. Conține informații despre stiva firului de execuție, prezentată ca o urmă de stivă. Așa cum este scris în text simplu, conținutul poate fi salvat pentru revizuire ulterioară. Analiza depozitelor de fire poate ajuta

  • Optimizarea performanței JVM
  • Optimizarea performanței aplicației
  • Diagnosticarea problemelor, de exemplu, un blocaj, conflict de fire etc.

Generarea de depozite de fire

Există multe modalități de a genera depozite de fire. Mai jos sunt câteva instrumente bazate pe JVM și pot fi executate din linia de comandă/terminalul (instrumente CLI) sau directorul /bin (instrumente GUI) din folderul de instalare al Java.

Să le explorăm.

#1. jStack

Cea mai simplă modalitate de a genera un dump de fir este folosind jStack. jStack este livrat cu JVM și poate fi utilizat din linia de comandă. Aici, avem nevoie de PID-ul procesului pentru care dorim să generăm dump-ul thread-ului. Pentru a obține PID, putem folosi comanda jps așa cum se arată mai jos.

jps -l

jps listează toate ID-urile de proces java.

Pe Windows

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

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

După cum putem vedea aici, obținem o listă a tuturor proceselor java care rulează. Conține id-ul VM local pentru procesul java care rulează și numele aplicației în coloanele unu și, respectiv, două. Acum, pentru a genera dump-ul thread-ului, folosim programul jStack cu flag –l care creează o ieșire listată lung a dump-ului. De asemenea, putem direcționa rezultatul către un fișier text la alegerea noastră.

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 este un instrument GUI care ne ajută să depanăm, să monitorizăm și să profilăm aplicațiile Java. De asemenea, vine cu JVM și poate fi lansat din directorul /bin al instalării noastre java. Este foarte intuitiv și ușor de utilizat. Printre alte opțiuni, ne permite, de asemenea, să captăm descărcarea firelor pentru un anumit proces.

  Cele mai bune 5 găzduiri de server Core Keeper pentru toată lumea

Pentru a vizualiza imaginea de descărcare a firului pentru un anumit proces, putem face clic dreapta pe program și selectați descărcarea firului din meniul contextual.

#3. jcmd

JCMD este un utilitar de linie de comandă care este livrat cu JDK și este utilizat pentru a trimite cereri de comandă de diagnosticare către JVM.

Cu toate acestea, funcționează numai pe mașina locală în care rulează aplicația Java. Poate fi folosit pentru a controla înregistrările de zbor Java, pentru a diagnostica și a depana aplicațiile JVM și Java. Putem folosi comanda Thread.print a jcmd pentru a obține o listă de depozite de fire pentru un anumit proces specificat de PID.

Mai jos este un exemplu despre cum putem folosi 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 înseamnă Java Mission Control. Este un instrument GUI open-source care este livrat cu JDK și este utilizat pentru a colecta și analiza datele aplicației Java.

Poate fi lansat din folderul /bin al instalării noastre Java. Administratorii și dezvoltatorii Java folosesc instrumentul pentru a aduna informații detaliate de nivel scăzut despre comportamentele JVM-urilor și ale aplicației. Permite analiza detaliată și eficientă a datelor colectate de Java Flight Recorder.

La lansarea jmc, putem vedea lista proceselor java care rulează pe mașina locală. Este posibilă și o conexiune la distanță. Pe un anumit proces, putem să facem clic dreapta și să alegem Începe înregistrarea zborului și apoi să verificăm depozitele firelor din fila Fire.

#5. jconsole

jconsole este un instrument Java Management Extension folosit pentru gestionarea și monitorizarea reclamațiilor.

De asemenea, are un set de operațiuni predefinite pe agentul JMX pe care utilizatorul le poate efectua. Acesta permite utilizatorului să detecteze și să analizeze urmele stivei unui program live. Poate fi lansat din folderul /bin al instalării noastre Java.

Folosind instrumentul GUI jconsole, putem inspecta urma stivei fiecărui fir atunci când îl conectăm la un proces java care rulează. Apoi, în fila Thread, putem vedea numele tuturor firelor care rulează. Pentru a detecta un blocaj, putem face clic pe Detectarea blocajului din partea dreaptă jos a ferestrei. Dacă este detectat un deadlock, acesta va apărea într-o filă nouă, altfel va fi afișat No Deadlock Detected.

  Cum să verificați starea unei reclamații FedEx

#6. ThreadMxBean

ThreadMXBean este interfața de gestionare a sistemului de fire al mașinii virtuale Java aparținând pachetului java.lang.Management. Este folosit în principal pentru a detecta firele care au intrat într-o situație de blocaj și pentru a obține detalii despre ele.

Putem folosi interfața ThreadMxBean pentru a captura programatic descărcarea firului. Metoda getThreadMXBean() a ManagementFactory este folosită pentru a obține o instanță a interfeței ThreadMXBean. Returnează numărul de fire de execuție live demon și non-daemon. ManagementFactory este o clasă din fabrică pentru obținerea bean-urilor gestionate pentru platforma 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 ();
}

Analiza manuală a depozitelor de fire

Analiza depozitelor de fire poate fi foarte utilă în identificarea problemelor în procesele cu mai multe fire. Probleme cum ar fi blocajele, conflictul de blocare și utilizarea excesivă a CPU de către dumpurile de fire individuale pot fi rezolvate prin vizualizarea stărilor de descărcare de fire individuale.

Debitul maxim din aplicație poate fi atins prin rectificarea stării fiecărui thread după analizarea filetului de descărcare.

De exemplu, să spunem, un proces consumă mult CPU, putem afla dacă vreun fir folosește cel mai mult CPU. Dacă există un astfel de fir, convertim numărul său LWP într-un număr hexazecimal. Apoi, din dump-ul firului, putem găsi firul cu nid egal cu numărul hexazecimal obținut anterior. Folosind urma stivei firului, putem identifica problema. Să aflăm id-ul procesului al firului folosind comanda de mai jos.

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

Să aruncăm o privire la partea de mai jos a filetului de descărcare. Pentru a obține descărcarea firului pentru procesul 26680, utilizați 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

Acum, să vedem care sunt lucrurile pe care le putem explora folosind depozitele de fire. Dacă observăm descărcarea firului, putem vedea o mulțime de conținut, care poate fi copleșitor. Cu toate acestea, dacă facem un pas la un moment dat, poate fi destul de simplu de înțeles. Să înțelegem prima linie

27-06-2020 09:01:29
Turnare completă a firelor virtuale Java HotSpot™ pe 64 de biți (mod mixt 25.221-b11):

Cele de mai sus afișează ora la care a fost generată dump-ul și informații despre JVM care a fost utilizat. În continuare, la final, putem vedea lista de fire, primul dintre ele este firul nostru ReferenceHandler.

Analizarea firelor blocate

Dacă analizăm jurnalele de descărcare a firelor de mai jos, putem constata că acesta a detectat fire cu starea BLOCKAT, ceea ce face ca performanța unei aplicații să fie foarte lentă. Deci, dacă putem găsi firele BLOCATE, putem încerca să extragem firele legate de blocările pe care încearcă să le obțină firele. Analiza urmei stivei din firul care ține în prezent blocarea poate ajuta la rezolvarea problemei.

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

Analizarea firelor blocate

O altă aplicație foarte frecvent utilizată a fileurilor de descărcare este detectarea blocajelor. Detectarea și soluționarea blocajelor poate fi mult mai ușoară dacă analizăm depozitele de fire.

Un blocaj este o situație care implică cel puțin două fire de execuție în care resursa necesară unui fir pentru a continua execuția este blocată de un alt thread și, în același timp, resursa necesară celui de-al doilea thread este blocată de primul thread.

Deci, niciunul dintre fire nu poate continua execuția, iar acest lucru duce la o situație de blocaj și se termină cu blocarea aplicației. Dacă sunt prezente dreadlock-uri, atunci secțiunea finală a firului de descărcare va tipări informațiile referitoare la deadlock după cum urmează.

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

Aici putem vedea informațiile de blocare într-un format destul de ușor de citit de om.

  7 Plugin-uri ChatGPT pentru scriitori pentru a lucra o oră în câteva minute

În afară de aceasta, dacă însumăm toate bucățile de mai sus de descărcare a firului împreună, atunci precizează informațiile de mai jos.

  • Managerul de referință este numele firului de execuție care poate fi citit de om.
  • #2 este id-ul unic al firului.
  • daemon denotă dacă firul este un fir daemon.
  • Prioritatea numerică a firului este dată de prio=10.
  • Starea curentă a firului este indicată de așteptarea cu condiție.
  • Apoi vedem urma stivei, care include informațiile de blocare.

Analizoare de descărcare a firelor

Pe lângă analiza manuală, există numeroase instrumente disponibile pentru analizarea fileurilor de descărcare, atât online, cât și offline. Mai jos sunt câteva dintre instrumentele enumerate, pe care le putem folosi în funcție de cerințe.

Mai întâi, să explorăm instrumentele online.

#1. Fir rapid

Fir rapid este instrumentul de analiză favorit al inginerului DevOps pentru a depana probleme complexe de producție. Acesta este un analizor online de descărcare a firelor de execuție Java, putem încărca descărcarea firului de execuție ca fișier sau putem copia și lipi direct descărcarea firului de execuție.

În funcție de dimensiune, va analiza descărcarea firului și va afișa informațiile așa cum se arată în captură de ecran.

Caracteristici

  • Depanați erorile JVM, încetinirile, scurgerile de memorie, înghețarile, vârfurile CPU
  • RCA instantaneu (nu așteptați furnizorii)
  • Tabloul de bord intuitiv
  • Suport API REST
  • Învățare automată

#2. Spotify Thread Dump Analyzer

The Spotify Thread Dump Analyzer este licențiat sub versiunea 2.0 a licenței Apache. Este un instrument online și acceptă dump-ul thread-ului ca fișier sau putem copia și lipi direct dump-ul thread-ului. În funcție de dimensiune, va analiza descărcarea firului și va afișa informațiile așa cum se arată în captură de ecran.

#3. Revizuire Jstack

Jstack.review analizează depozitele de fire java din browser. Această pagină este doar pentru client.

#4. Site 24×7

Acest instrument este o condiție prealabilă pentru detectarea firelor de execuție defecte care degradează performanța mașinii virtuale Java (JVM). Probleme cum ar fi blocajele, conflictul de blocare și utilizarea excesivă a CPU de către dumpurile de fire individuale pot fi rezolvate prin vizualizarea stărilor de descărcare de fire individuale.

Debitul maxim din aplicație poate fi atins prin rectificarea stării fiecărui fir furnizat de instrument.

Acum, să explorăm instrumentele offline.

Când vine vorba de profilare, doar cel mai bun instrument este suficient de bun.

#1. JProfiler

JProfiler este unul dintre cele mai populare analizoare de descărcare a firelor în rândul dezvoltatorilor Java. Interfața de utilizare intuitivă a JProfiler vă ajută să rezolvați blocajele de performanță, să identificați scurgerile de memorie și să înțelegeți problemele legate de threading.

JProfiler acceptă crearea de profil pe următoarele platforme:

  • Windows
  • macOS
  • Linux
  • FreeBSD
  • Solaris
  • AIX
  • HP-UX

Mai jos sunt câteva caracteristici care fac din JProfiler cea mai bună alegere pentru profilarea aplicațiilor noastre pe JVM.

Caracteristici

  • Suporta profilarea bazei de date pentru JDBC, JPA și NoSQL
  • Este disponibil și suport pentru ediția Java Enterprise
  • Prezintă informații de nivel înalt despre apelurile RMI
  • Analiza stelară a scurgerilor de memorie
  • Capacități extinse de QA
  • Profilatorul de filet integrat este strâns integrat cu vizualizările de profilare a CPU.
  • Suport pentru platforme, IDE-uri și servere de aplicații.

#2. IBM TMDA

IBM Thread and Monitor Dump Analyzer pentru Java (TMDA) este un instrument care permite identificarea blocajelor, blocajelor, conflictelor de resurse și blocajelor în depozitele de fire Java. Este un produs IBM, dar instrumentul TMDA este furnizat fără nicio garanție sau suport; cu toate acestea, ei încearcă să repare și să îmbunătățească instrumentul în timp.

#3. ManageEngine

ManageEngine Managerul de aplicații poate ajuta la monitorizarea memoriei JVM Heap și Non-Heap. Putem chiar să configuram praguri și să fim alertați prin e-mail, SMS etc. și să ne asigurăm că o aplicație Java este bine reglată.

#4. YourKit

YourKit constă din produsele de mai jos numite kit.

  • Java Profiler – Profiler cu supraîncărcare redusă complet pentru platformele Java EE și Java SE.
  • YouMonitor – Monitorizarea performanței și profilarea lui Jenkins, TeamCity, Gradle, Maven, Ant, JUnit și TestNG.
  • .NET Profiler – Performanță ușor de utilizat și profiler de memorie pentru .NET framework.

Concluzie

Acum știți cum sunt utile depozitele de fire pentru înțelegerea și diagnosticarea problemelor în aplicațiile cu mai multe fire. Cu adecvat cunoştinţeîn ceea ce privește depozitele de fire – structura lor, informațiile conținute în ele etc. – le putem utiliza pentru a identifica rapid cauzele problemelor.

x