Шта је Тхреад Думп и како их анализирати?

Hajde da se posvetimo temi dumpova niti i njihovoj analizi.

Takođe, razmotrićemo kako nam dumpovi niti pomažu u identifikaciji problema, kao i neke analizatore koje možete koristiti u tu svrhu.

Šta je to Nit (Thread)?

Proces je računarski program koji se učitava u memoriju računara i potom izvršava. Izvršavanje procesa se odvija pomoću procesora ili skupa procesora. U memoriji, proces je opisan skupom ključnih informacija, uključujući skladište promenljivih, deskriptore datoteka, programski brojač, registre, signale i druge bitne podatke.

Proces se može sastojati od nekoliko lakših procesa, poznatih kao niti. Ovo omogućava postizanje paralelizma, gde se proces deli na više niti. Ovakav pristup rezultira boljim performansama. Sve niti unutar istog procesa dele zajednički memorijski prostor i međusobno su zavisne.

Dumpovi Niti

Tokom izvršavanja procesa, možemo utvrditi trenutno stanje izvršavanja niti unutar tog procesa pomoću dumpova niti. Dump niti predstavlja snimak svih aktivnih niti u datom trenutku izvršavanja programa. On sadrži sve relevantne informacije o svakoj niti i njenom trenutnom statusu.

Savremene aplikacije često koriste veliki broj niti. Svaka nit zahteva određene resurse i obavlja specifične zadatke povezane sa procesom. Ovo može poboljšati performanse aplikacije jer niti mogu iskoristiti dostupna CPU jezgra.

Međutim, postoje i kompromisi. Na primer, više niti ponekad ne koordinira dobro, što može dovesti do zastoja. Ako dođe do problema, dumpovi niti mogu nam pomoći da ispitamo stanje naših niti i utvrdimo uzrok problema.

Dump Niti u Javi

Dump JVM niti je lista stanja svih niti koje su aktivne u okviru procesa u datom trenutku. On uključuje informacije o steku niti, koje se prikazuju kao trag steka. Pošto je dump zapisan u otvorenom tekstu, sadržaj se može sačuvati za kasniju analizu. Analiza dumpova niti je korisna za:

  • Optimizaciju performansi JVM-a
  • Optimizaciju performansi aplikacije
  • Dijagnostikovanje problema, kao što su zastoji i konflikti među nitima

Generisanje Dumpova Niti

Postoji nekoliko načina za generisanje dumpova niti. Neki alati su zasnovani na JVM-u i mogu se pokrenuti iz komandne linije/terminala (CLI alati), dok se drugi nalaze u direktorijumu /bin (GUI alati) unutar instalacionog direktorijuma Jave.

Pogledajmo neke od njih.

#1. jstack

Najjednostavniji način za generisanje dumpa niti je korišćenje jstack-a. Ovaj alat dolazi uz JVM i može se koristiti iz komandne linije. Da bismo ga koristili, potreban nam je PID procesa za koji želimo da generišemo dump. PID možemo dobiti pomoću komande jps, kao što je prikazano ispod.

jps -l

jps prikazuje listu svih ID-ova Java procesa.

Na Windows-u

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

Na Linux-u

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

Kao što vidimo, dobijamo listu svih pokrenutih Java procesa. U kolonama su prikazani lokalni VM ID za pokrenuti Java proces, kao i naziv aplikacije. Da bismo generisali dump niti, koristimo jstack program sa zastavicom -l koja kreira detaljan izlaz dumpa. Takođe, možemo preusmeriti izlaz u tekstualnu datoteku po želji.

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 je GUI alat koji nam pomaže pri rešavanju problema, praćenju i profilisanju Java aplikacija. Takođe dolazi sa JVM-om i može se pokrenuti iz /bin direktorijuma naše Java instalacije. Veoma je intuitivan i jednostavan za korišćenje. Između ostalih opcija, omogućava nam i da snimimo dump niti za određeni proces.

Da bismo videli dump niti za određeni proces, možemo desnim klikom miša odabrati „Thread Dump“ iz kontekstnog menija.

#3. jcmd

jcmd je alat komandne linije koji se isporučuje sa JDK-om i služi za slanje dijagnostičkih komandi JVM-u.

Međutim, radi samo na lokalnoj mašini na kojoj je pokrenuta Java aplikacija. Može se koristiti za kontrolu Java Flight Recordings, dijagnostikovanje i rešavanje problema sa JVM-om i Java aplikacijama. Možemo koristiti komandu Thread.print iz jcmd-a da dobijemo listu dumpova niti za određeni proces naveden u PID-u.

U nastavku je primer kako možemo da koristimo 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, odnosno Java Mission Control, je GUI alat otvorenog koda koji dolazi sa JDK-om i koristi se za prikupljanje i analizu podataka Java aplikacije.

Može se pokrenuti iz /bin foldera naše Java instalacije. Java administratori i programeri koriste ovaj alat za prikupljanje detaljnih informacija niskog nivoa o ponašanju JVM-a i aplikacija. Omogućava detaljnu i efikasnu analizu podataka koje prikuplja Java Flight Recorder.

Kada pokrenemo jmc, možemo videti listu Java procesa koji se izvršavaju na lokalnoj mašini. Moguće je i uspostaviti udaljenu vezu. Za odabrani proces, desnim klikom miša izaberemo „Start Flight Recording“, a zatim proveravamo dump niti u tabu „Threads“.

#5. jconsole

jconsole je alatka Java Management Extension koja se koristi za upravljanje i praćenje Java aplikacija.

Ona ima skup unapred definisanih operacija na JMX agentu koje korisnik može izvršiti. Omogućava korisniku da otkrije i analizira praćenje steka programa uživo. Može se pokrenuti iz /bin foldera naše Java instalacije.

Koristeći jconsole GUI alat, možemo pregledati praćenje steka svake niti kada ga povežemo sa pokrenutim Java procesom. Zatim, u tabu „Threads“, možemo videti nazive svih pokrenutih niti. Da bismo otkrili zastoj, možemo kliknuti na „Detect Deadlock“ u donjem desnom uglu prozora. Ako je zastoj detektovan, pojaviće se u novom tabu, u suprotnom će biti prikazana poruka „No Deadlock“.

#6. ThreadMXBean

ThreadMXBean je interfejs za upravljanje sistemom niti Java virtuelne mašine, koji pripada paketu java.lang.management. Uglavnom se koristi za otkrivanje niti koje su upale u situaciju zastoja i dobijanje detalja o njima.

Možemo koristiti ThreadMXBean interfejs da programski snimimo dump niti. Metoda getThreadMXBean() iz ManagementFactory koristi se za dobijanje instance ThreadMXBean interfejsa. Vraća broj živih demonskih i ne-demonskih niti. ManagementFactory je fabrika klasa za dobijanje upravljanih beanova za Java platformu.

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

Ručna Analiza Dumpova Niti

Analiza dumpova niti može biti veoma korisna u identifikaciji problema u višenitnim procesima. Problemi poput zastoja, konflikata oko zaključavanja i prekomernog korišćenja CPU-a od strane pojedinačnih niti mogu se rešiti vizualizacijom stanja svake niti.

Maksimalna propusnost aplikacije može se postići ispravljanjem statusa svake niti nakon analize dumpa.

Na primer, recimo da proces koristi mnogo CPU-a. Možemo saznati da li neka nit koristi najviše CPU resursa. Ako takva nit postoji, njen LWP broj pretvaramo u heksadecimalni broj. Zatim u dumpu niti pronalazimo nit sa nid (Native ID) koji odgovara prethodno dobijenom heksadecimalnom broju. Koristeći trag steka niti, možemo precizno odrediti problem. Pogledajmo kako možemo saznati ID procesa niti pomoću sledeće komande.

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

Pogledajmo sada dump niti. Da bismo dobili dump niti za proces 26680, koristimo komandu `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

Hajde sada da vidimo koje informacije možemo istražiti pomoću dumpova niti. Ako pogledamo dump, videćemo mnogo teksta koji može izgledati preobimno. Međutim, ako pristupimo korak po korak, može postati prilično razumljiv. Razmotrimo prvi red:

2020-06-27 09:01:29
Dump pune niti Java HotSpot(TM) 64-bitni Server VM (mešoviti režim 25.221-b11):

Gore prikazano vreme kada je dump generisan i informacije o korišćenom JVM-u. Zatim, na kraju, vidimo listu niti. Prva među njima je naša „ReferenceHandler“ nit.

Analiza Blokiranih Niti

Ako analiziramo sledeće zapise dumpa niti, možemo otkriti da je detektovao niti sa statusom „BLOCKED“, što dovodi do sporih performansi aplikacije. Ako uspemo da pronađemo „BLOCKED“ niti, možemo pokušati da izvučemo informacije o bravama koje niti pokušavaju da dobiju. Analiza traga steka iz niti koja trenutno drži bravu može nam pomoći u rešavanju problema.

[[email protected] ~]# jstack -l 26680
.
.
.
.
" DB-Processor-13" daemon prio