Alati za dijagnostiku i pronalaženje problema u Javi (pregled JVM parametara, korišćenja memorije i analize)
Alati koji dolaze uz JDK
U bin direktorijumu JDK-a nalazi se mnogo komandnolinijskih alata:
$ ls -l $JAVA_HOME/bin | grep -E 'javap|jcmd|jconsole|jhat|jinfo|jmap|jps|jstack|jstat|jvisualvm'
-rwxr-xr-x 1 root root 10724 Jul 14 2019 javap
-rwxr-xr-x 1 root root 10724 Jul 14 2019 jcmd
-rwxr-xr-x 1 root root 11021 Jul 14 2019 jconsole
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jhat
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jinfo
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jmap
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jps
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jstack
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jstat
-rwxr-xr-x 1 root root 10721 Jul 14 2019 jvisualvmMožemo videti da su veličine pojedinih alata uglavnom stabilne na oko 10 KB; to nije toliko namerno od strane tima koji razvija JDK, koliko posledica toga što su većina ovih alata samo tanki omotač oko biblioteke jdk\lib\tools.jar, čija se glavna funkcionalnost realizuje u okviru tools biblioteke.
Prednost komandnolinijskih alata je sledeća: kada se aplikacija postavi u produkciono okruženje, bilo da je u pitanju direktan pristup fizičkom serveru ili daljinski telnet na server, pristup je ograničen. Ali pomoću interfejsa u tools.jar biblioteci možemo neposredno u aplikaciji ostvariti snažnu funkcionalnost praćenja i analize.
Često korišćene komande:
Ovde ćemo pre svega predstaviti sledeće alate:
jps: pregled informacija o java procesima na lokalnoj mašini
jstack: ispisuje podatke o steku niti, pravi thread dump fajl
jmap: ispisuje podatke o memorijskom mapiranju, pravi heap dump fajl
jstat: alat za praćenje performansi
jhat: alat za analizu memorije, služi za raščlanjivanje heap dump fajla i prikaz na način pogodan za čitanje
jconsole: jednostavan vizuelni alat za JVM
jvisualvm: znatno moćniji vizuelni alat za JVM
javap: pregled bytecode-a
JAVA Dump:
JAVA Dump je snimak stanja virtuelne mašine u toku rada; čuva stanje i podatke virtuelne mašine u fajl, uključujući:
Thread dump: sadrži stanje rada svih niti, u obliku običnog teksta
Heap dump: sadrži stanje svih objekata na hipu, u binarnom obliku
1. jps
Komanda koja prikazuje PID-ove svih tekućih java procesa; pomoću nje možemo videti koliko je java procesa zapravo pokrenuto (jer svaki java program zauzima sopstvenu instancu java virtuelne mašine). Mana jps-a je, međutim, što može prikazati samo PID-ove procesa tekućeg korisnika; za procese drugih korisnika i dalje moramo koristiti Linux komandu ps.

Izvršavanjem komande jps ispisuju se svi java procesi u toku; i sama komanda jps jeste java program. Broj ispred je upravo identifikator procesa — taj id je od velikog značaja, a više o njemu biće reči kasnije.
jps -help:

jps -l ispisuje potpuno ime paketa main.class aplikacije ili potpunu putanju do JAR fajla aplikacije

jps -v ispisuje argumente prosleđene JVM-u

jps otkazuje
Pri pronalaženju problema možemo naići na situaciju u kojoj jps ne može da prikaže identifikator procesa, dok komanda ps -ef | grep java jasno vidi pokrenuti java proces.
Da bismo objasnili ovu pojavu, hajde da prvo razumemo mehanizam rada jps-a:
Nakon pokretanja, java program će u direktorijumu /tmp/hsperfdata_{userName}/ generisati nekoliko fajlova čije je ime upravo PID java procesa; stoga jps, da bi ispisao identifikatore procesa, zapravo samo izlista imena fajlova u ovom direktorijumu, a podatke o sistemskim parametrima čita iz sadržaja tih fajlova.
Razmislimo: ukoliko zbog popunjenosti diska nije moguće kreirati te fajlove, ili korisnik nema pravo čitanja nad njima; ili pak iz nekog razloga ti fajlovi ili direktorijumi budu obrisani — sve te situacije dovešće do toga da komanda jps otkaže.
Ako jps otkaže, a mi ipak moramo doći do PID-a, možemo se poslužiti i sa još dve metode:
1,top | grep java
2,ps -ef |grep java2. jstack
Pre svega služi za generisanje snimka niti određenog procesa u tekućem trenutku; snimak niti jeste zbir stekova metoda koje u trenutnoj Java virtuelnoj mašini svaka nit izvršava. Glavni cilj generisanja snimka niti jeste lociranje uzroka dužih zastoja niti, kao što su međusobno zaključavanje (deadlock), beskonačne petlje ili dugotrajno čekanje na eksterne resurse.

3. jmap
Pre svega služi za ispis detalja memorijskog mapiranja deljenih objekata ili hipa određenog java procesa.
Heap Dump je memorijski snimak koji odražava stanje korišćenja hipa, a obuhvata sistemske podatke, osobine virtuelne mašine, kompletan thread Dump, stanje svih klasa i objekata itd. Obično, pri nestašici memorije, anomalijama GC-a i sl. posumnjamo u curenje memorije, pa tada idemo na ispis Heap Dump-a.
Zbirka upotrebe jmap-a:

1. jmap pid
$ jmap 2972
0x00007f9e8c000000 2744K /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjava.so
0x00007f9e8c2b00000 115K /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnet.so
0x00007f9e8c2ce0000 107K /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libnio.so
0x00007f9e8c4c00000 67224K /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar
...Ispisani podaci redom su: početna adresa deljenog objekta, veličina mapiranja i puna putanja do deljenog objekta.
2. jmap -heap pid: pregled korišćenja hipa
$ jmap -heap 2972
Attaching to process ID 2972, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
using thread-local object allocation.
Mark Sweep Parallel GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2095054848 (1998.0MB)
NewSize = 699050496 (666.625MB)
MaxNewSize = 699050496 (666.625MB)
OldSize = 1396007936 (1331.375MB)
NewRatio = 2
SurvivorRatio = 8
Heap Usage:
PS Young Generation
Eden Space:
capacity = 524288000 (500.0MB)
used = 164167448 (156.61MB)
free = 360120552 (343.39MB)
31.32% used
...3. jmap -histo pid: pregled broja objekata na hipu i njihovih veličina

Ispisani podaci redom su: serijski broj, broj objekata, memorijska zauzetost tih objekata i potpuno kvalifikovano ime klase kojoj ti objekti pripadaju.
Ukoliko je u pitanju unutrašnja klasa, ispred imena klase staje *; ukoliko se doda podargument live, kao u jmap -histo:live pid, ova komanda će izazvati jedan Full GC i brojaće samo preživele objekte.
4. jmap -dump:format=b,file=heapdump pid: izvoz detaljnih podataka o korišćenju memorije u fajl
Zatim se fajl pregleda komandom jhat: jhat -port 4000 ime_fajla, a u pregledaču se pristupa na http://localhost:4000/.
Zaključak:
Ova komanda se koristi u scenarijima u kojima programu ponestane memorije ili je GC učestao — tada je veoma verovatno curenje memorije. Pomoću gore navedenih komandi možemo pregledati korišćenost hipa, postojano referenciranje velikog broja objekata i sl.
4. jstat
Pre svega služi za praćenje u realnom vremenu, preko komandne linije, resursa i performansi Java aplikacije, uključujući praćenje veličine hipa i stanja garbage collection-a.
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
option: opcije koje često koristimo su gc i gcutil
vmid: identifikator java procesa
interval: interval, u milisekundama
count: broj ispisivanja
1. jstat -gc PID 5000 20

S0C: kapacitet prvog survivor-a mlade generacije (bajtovi)
S1C: kapacitet drugog survivor-a mlade generacije (bajtovi)
S0U: iskorišćen kapacitet prvog survivor-a mlade generacije (bajtovi)
S1U: iskorišćen kapacitet drugog survivor-a mlade generacije (bajtovi)
EC: prostor Eden-a u mladoj generaciji (bajtovi)
EU: iskorišćen prostor Eden-a u mladoj generaciji (bajtovi)
OC: kapacitet stare generacije (bajtovi)
OU: iskorišćen prostor u staroj generaciji (bajtovi)
PC: kapacitet PermGen-a
PU: iskorišćen kapacitet PermGen-a
YGC: broj GC-a u mladoj generaciji od pokretanja aplikacije do trenutka uzorkovanja
YGCT: vreme utrošeno na GC u mladoj generaciji od pokretanja aplikacije do trenutka uzorkovanja (jedinica: s)
FGC: broj GC-a (Full GC) u staroj generaciji od pokretanja aplikacije do trenutka uzorkovanja
FGCT: vreme utrošeno na GC u staroj generaciji od pokretanja aplikacije do trenutka uzorkovanja (jedinica: s)
2. jstat -gcutil PID 5000 20
$ jstat -gcutil 2972 5000 20
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 87.62 53.97 47.20 98.56 148 1.234 3 0.331 1.565
0.00 87.62 55.06 47.20 98.56 148 1.234 3 0.331 1.565
91.44 0.00 48.66 47.23 98.56 149 1.246 3 0.331 1.577
0.00 92.30 51.02 47.23 98.56 150 1.257 3 0.331 1.588
...s0: procenat iskorišćenog u odnosu na tekući kapacitet prvog survivor-a mlade generacije
s1: procenat iskorišćenog u odnosu na tekući kapacitet drugog survivor-a mlade generacije
E: procenat iskorišćenog Eden-a u mladoj generaciji u odnosu na tekući kapacitet
O: procenat iskorišćenog u staroj generaciji u odnosu na tekući kapacitet
P: procenat iskorišćenog u PermGen-u u odnosu na tekući kapacitet
5. jhat
Pre svega služi za raščlanjivanje Java heap dump-a i pokretanje web servera, nakon čega se heap dump fajl može pregledati u pregledaču.
Način generisanja dump fajla već je opisan ranije; ovde će pre svega biti reči o tome kako raščlaniti Java heap dump fajl i pokrenuti web server.
jhat heapdump

Ova komanda pretvara heapdump fajl u HTML format i pokreće http uslugu, podrazumevano na portu 7000.
Ukoliko dođe do sukoba portova, port se može zadati komandom: jhat -port 4000 heapdump
Pristupimo sada: ip:port

6. jinfo
jinfo se može koristiti za pregled proširenih parametara Java aplikacije u radu, a podržava čak i delimično dinamičko menjanje pojedinih parametara u toku rada.
Osnovna sintaksa korišćenja glasi: jinfo -< option > < pid >, gde option može biti:
-flag< name >: ispisuje vrednost parametra zadate Java virtuelne mašine
-flag [+|-]< name >: postavlja ili poništava logičku vrednost parametra zadate Java virtuelne mašine
-flag < name >=< value >: postavlja vrednost parametra zadate Java virtuelne mašine
Primer upotrebe
Sledeća komanda prikazuje maksimalnu starost objekata mlade generacije koja se unapređuje u objekte stare generacije. Pri pokretanju programa ovaj parametar nije bio zadat, ali pomoću jinfo možemo videti njegovu tekuću vrednost.

Sledeća komanda prikazuje da li se ispisuju detaljni GC podaci:

Sledeća komanda za vreme rada aplikacije dinamički uključuje prekidač za ispis detaljnih GC podataka:

Napomena: iako jinfo može dinamički menjati parametre virtuelne mašine dok Java program radi, ne podržava sve parametre za dinamičku izmenu.
7. jcmd
Posle JDK 1.7 dodat je novi komandnolinijski alat jcmd. To je višenamenski alat koji se može koristiti za izvoz hipa, pregled java procesa, izvoz informacija o nitima, izvršavanje GC-a itd. jcmd obuhvata većinu funkcionalnosti jmap-a, a Oracle zvanično preporučuje korišćenje jcmd-a umesto jmap-a.
Komandom jcmd -l izlistavaju se sve virtuelne mašine koje trenutno rade, primer: 
Za svaku virtuelnu mašinu, komandom help mogu se izlistati sve komande koje ona podržava, primer:

Značenja podkomandi:
- VM.native_memory
- VM.commercial_features
- GC.rotate_log
- ManagementAgent.stop
- ManagementAgent.start_local
- ManagementAgent.start
- Thread.print, ispisuje podatke o steku niti
- GC.class_histogram, prikazuje statistiku klasa u sistemu
- GC.heap_dump, izvozi podatke o hipu, ista funkcija kao jmap -dump
- GC.run_finalization, pokreće finalize()
- GC.run, pokreće gc()
- VM.uptime, vreme od pokretanja VM-a
- VM.flags, dobija JVM parametre pri pokretanju
- VM.system_properties, dobija sistemsko Properties
- VM.command_line, parametri navedeni u komandnoj liniji pri pokretanju
- VM.version
- help
Primer:

8. Vizuelni alati za praćenje (JConsole, JVisualVM)
Uvod
U bin fascikli instalacionog direktorijuma JDK-a, pored komandnolinijskih alata za praćenje, nalaze se i neki vizuelni alati za praćenje koji korisnicima omogućavaju da intuitivno sagledaju stanje virtuelne mašine u radu. Često korišćeni vizuelni alati za praćenje su sledeći:
JConsole
Uvod
JConsole (Java Monitoring and Management Console) vizuelni je alat zasnovan na JMX-u (Java Management Extensions). Njegova glavna funkcija jeste prikupljanje sistemskih podataka i dinamičko podešavanje sistemskih parametara preko MBean-ova (Managed Bean) iz JMX-a. JMX (Java Management Extensions) jeste okvir koji aplikacijama, uređajima, sistemima itd. ugrađuje funkcionalnost upravljanja i obično se koristi za praćenje stanja rada sistema ili upravljanje pojedinim funkcijama sistema.
Upotreba
Nakon otvaranja programa jconsole iz bin direktorijuma, on automatski skenira sve JVM procese na tekućem računaru.
Nakon odabira procesa koji želite da praćite, kliknite na dugme za povezivanje (Connect) i ući ćete u interfejs za praćenje. Interfejs za praćenje sadrži šest kartica: Overview, Memory, Threads, Classes, VM Summary i MBeans. Kartica Overview prikazuje zbirne podatke kartica Memory, Threads i Classes — četiri živa grafikona zauzeća heap-a, broja niti, broja klasa i CPU-a.
Kartica Memory pre svega služi za prikaz iskorišćenosti pojedinih oblasti na hipu i van hipa (više o tome u poglavlju o vizuelnim alatkama).
Kartica Threads pre svega prikazuje podatke o steku pojedinih niti, a u donjem uglu nalazi se dugme Detect Deadlock; nakon klika, ukoliko se otkrije mrtva petlja, pored kartice Threads u donjem delu pojaviće se posebna kartica sa mrtvom petljom. Klikom na nju mogu se videti niti koje su izazvale mrtvu petlju — koje bravicu drže, a koje je čekaju.
Kartica Classes pre svega služi za prikaz broja trenutno učitanih i izbačenih klasa, dok kartica VM Summary pre svega služi za prikaz relevantnih parametara virtuelne mašine (tip i verzija VM-a, argumenti, veličine heap-a itd.).
VisualVM
Uvod
VisualVM (All-in-One Java Troubleshooting Tool) jedan je od najmoćnijih Oracle-ovih alata za praćenje rada i rešavanje problema; pored uobičajenog praćenja rada i rešavanja problema, može se koristiti i za analizu performansi (Profiling). Pošto je VisualVM zasnovan na NetBeans platformi kao razvojni alat, podržava i proširenje funkcionalnosti putem dodataka. Glavne funkcije VisualVM-a su:
- Prikaz procesa virtuelne mašine sa njegovim podacima o konfiguraciji i okruženju (slično funkciji jps, jinfo);
- Praćenje podataka o procesoru, garbage collection-u, hipu, method area i nitima aplikacije (slično funkciji jstat, jstack);
- Izvoz i analiza snimka hipa (slično funkciji jmap, jhat);
- Analiza performansi programa na nivou metoda, pronalaženje metoda koji se najviše pozivaju i najduže izvršavaju;
- Snimak programa van mreže: može prikupiti podatke o konfiguraciji u toku rada, thread dump, memory dump itd. radi izrade snimka.
Upotreba
Nakon otvaranja programa jvisualvm iz bin direktorijuma, on automatski skenira sve JVM procese na tekućem računaru (panel Applications → Local).
Klikom na proces koji želite da praćite, s desne strane će se prikazati relevantni podaci praćenja.
1. Heap Dump
U interfejsu za praćenje (kartica Monitor), klikom na dugme Perform GC može se izvršiti garbage collection, a klikom na Heap Dump napraviti Heap Dump. Nakon heap dump-a prikazaće se i rezultati analize (zauzece po klasama, instance itd.).
2. Thread Dump
U kartici Threads može se pregledati stanje svih niti; ukoliko nastane mrtva petlja, ovaj interfejs će i to naznačiti (crvena nit Blocked, uz poruku „Deadlock detected“).
Tada se može izvršiti Thread Dump (dugme Thread Dump) da bi se dobili konkretni podaci o nitima, slično efektu komande jstack:
"main" #1 prio=5 os_prio=0 tid=0x00007f9e8c00a000 nid=0x2a31 waiting on condition [0x00007f9e940f5000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.example.Demo.main(Demo.java:12)
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f9e8c09b000 nid=0x2a3f waiting for monitor entry [0x00007f9e92f4c000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.DeadLockDemo.lambda$main$1(DeadLockDemo.java:22)
- waiting to lock <0x000000076ab4f780> (a java.lang.Object)
- locked <0x000000076ab4f770> (a java.lang.Object)3. Analiza performansi
U interfejsu Profiler može se vršiti analiza performansi CPU-a i memorije. Da bi analiza performansi počela, potrebno je prvo izabrati jedno od dugmadi CPU ili Memory; VisualVM će tada početi da beleži sve metode koje je aplikacija izvršila: ukoliko je u pitanju analiza procesorskog vremena, vodiće statistiku o broju izvršavanja i utrošenom vremenu pojedinih metoda; ukoliko je u pitanju analiza memorije, vodiće statistiku o broju objekata povezanih sa svakim metodom i prostoru koji ti objekti zauzimaju. Da biste završili analizu performansi, dovoljno je kliknuti dugme Stop.
4. Visual GC
Panel Visual GC podrazumevano nije prikazan; za njega je potrebno proširenje putem dodatka. On u realnom vremenu praći stanje virtuelne mašine, a funkcionalno je sličan komandi jstat — prikazuje oblasti Eden, Survivor 0/1, Old Gen i Metaspace kao žive stubiće koji se pune i prazne pri svakom GC-u.
Instalacija dodataka
U glavnom interfejsu, klikom na Tools → Plugins otvara se panel dodataka. U kartici Available Plugins označi željeni dodatak i kliknite Install; desnim klikom na opciju dodatka ili klikom na dugme za instalaciju može se dovršiti instalacija odgovarajućeg dodatka.
Treba imati na umu da je, pre instalacije dodataka, potrebno konfigurisati centar dodataka prema svojoj verziji JVM-a, u protivnom će biti bačen izuzetak „Nije moguće povezati se sa centrom dodataka". Centar dodataka za svaku verziju može se pregledati na sledećoj adresi: https://visualvm.github.io/pluginscenters.html.
Zatim je potrebno ispravnu adresu centra dodataka konfigurisati u programu: Tools → Plugins → Settings, kliknite na Edit pored centra i zameni URL adresu onom koja odgovara tvojoj verziji VisualVM-a.
Povezivanje na udaljene procese
Gore, pri demonstraciji JConsole-a i VisualVM-a, koristili smo lokalne procese, ali u praksi mnogo češće moramo da praćimo udaljene procese na serveru. Da bismo praćili procese na udaljenom računaru, potrebne su odgovarajuće JXS konfiguracije; prema tome da li pri povezivanju treba korisničko ime i lozinka, mogu se razlikovati dva načina konfiguracije:
Bez bezbednosnih akreditiva
Pri pokretanju Java procesa na serveru dodajte sledeće parametre:
java -Dcom.sun.management.jmxremote.port=12345 # port za JMX udaljenu konekciju
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-jar springboot.jarU ovom slučaju za povezivanje je dovoljno znati adresu računara i port, bez korisničkog imena i lozinke, pa je bezbednost relativno niska.
Uz bezbednosne akreditive
Pri pokretanju Java procesa na serveru dodajte sledeće parametre:
java -Dcom.sun.management.jmxremote.port=12345
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.access.file=/usr/local/jmxremote.access
-Dcom.sun.management.jmxremote.password.file=/usr/local/jmxremote.password
-jar springboot.jarSadržaj fajla jmxremote.access je sledeći, gde je admin korisničko ime, a readwrite označava dozvolu i za čitanje i za pisanje; može se postaviti i na readonly (samo čitanje):
admin readwriteSadržaj fajla jmxremote.password je sledeći, gde je admin korisničko ime, a 123456 lozinka:
admin 123456Nakon kreiranja oba fajla, potrebno je dodeliti im pravo izvršenja:
chmod 600 /usr/local/jmxremote.access
chmod 600 /usr/local/jmxremote.password
chown root:root /usr/local/jmxremote.access
chown root:root /usr/local/jmxremote.passwordZatim, pri udaljenom povezivanju pomoću VisualVM-a: u panelu Remote desni klik → Add Remote Host (unesite adresu servera), zatim desni klik na host → Add JMX Connection i unesite host:12345 — pri čemu je port vrednost konfigurisanog Dcom.sun.management.jmxremote.port, a ne port Java programa. Nakon uspešnog povezivanja može se videti stanje praćenja odgovarajućeg procesa.
Ostali alati
JOL (Java Object Layout): biblioteka koju pruža OpenJDK, služi za pregled memorijskog rasporeda Java objekata; vrlo je korisna i može se uz nju pratiti procesi kao što je unapređenje brava. Dovoljno je dodati Maven zavisnost i može se koristiti, primer:
// zavisnost
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
// kod
class TTTT {
public static void main(String[] args) {
System.err.println(ClassLayout.parseInstance(new Person()).toPrintable());
System.err.println(ClassLayout.parseClass(Person.class).toPrintable());
}
}
class Person {
private int age = 1;
private String name = "zhangsan";
}
// rezultat izvršenja koda
com.marchon.learning.Person object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000005e4c804101 (hash: 0x5e4c8041; age: 0)
8 4 (object header: class) 0xf8010dd9
12 4 int Person.age 1
16 4 java.lang.String Person.name (object)
20 4 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalOpenJDK izvorni kod: pregled implementacije JDK nativnih metoda
strace: praćenje sistemskih poziva tokom rada programa
https://fastthread.io: sajt za analizu steka niti — uploadujete thread dump fajl i dobijate pregled stanja niti, mrtvih petlji i najtoplijih metoda.
Smernice za pronalaženje problema (opšte)
Korišćenje diska: komanda du
Korišćenje memorije: komanda free
Korišćenost CPU-a: komanda top
Korišćenje mreže: komanda netstat
Analiza problema Java programa: jmap za analizu memorije hipa, jstack za analizu steka niti itd. — vidi prethodni tekst.
