You use the
jstack
command to print Java stack traces of Java threads for a specified Java process. This command is experimental and unsupported.
官方文檔中,jstack
是用于打印指定Java進(jìn)程的線程堆棧跟蹤展姐,我們通常用jstack來(lái)分析死鎖和死循環(huán)等場(chǎng)景。
使用方式及參數(shù)
Usage:
jstack [-l][-e] <pid>
(to connect to running process)
Options:
-l long listing. Prints additional information about locks
-e extended listing. Prints additional information about threads
-? -h --help -help to print this help message
使用方式是找到Java進(jìn)程id——pid俩莽,jstack pid
就能打印出堆棧信息。-l
參數(shù)可以打印鎖的信息乔遮,-e
可以打印線程的額外信息扮超。
jstack分析死鎖
我們寫(xiě)一個(gè)死鎖的demo DeadlockDemo.java
并且運(yùn)行:
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1 acquired lock1");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("Thread 1 acquired lock2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread 2 acquired lock2");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("Thread 2 acquired lock1");
}
}
});
t1.start();
t2.start();
// 等待兩個(gè)線程執(zhí)行完畢
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread finished");
使用jps -l
查看java程序的端口
jps -l
12406 org.jetbrains.jps.cmdline.Launcher
2156 com.intellij.idea.Main
17119 jdk.jcmd/sun.tools.jps.Jps
命令行執(zhí)行jstack -l -e 12406
2024-02-15 15:30:11
Full thread dump OpenJDK 64-Bit Server VM (17.0.9+9-Ubuntu-120.04 mixed mode, sharing):
第一部分是虛擬機(jī)信息,SMR全稱(chēng)是Safe Memory Reclamation蹋肮,即jvm安全分配的線程:
Threads class SMR info:
_java_thread_list=0x00007f102c001740, length=14, elements={
0x00007f10c0013af0, 0x00007f10c01765a0, 0x00007f10c0177990, 0x00007f10c017e170,
0x00007f10c017f530, 0x00007f10c0180950, 0x00007f10c0182310, 0x00007f10c0183850,
0x00007f10c018ccc0, 0x00007f10c01985d0, 0x00007f10c01c53c0, 0x00007f10c04d9610,
0x00007f10c04e48d0, 0x00007f102c000d20
}
第二部分是線程堆棧信息出刷,堆棧信息里第一行是線程的元信息:
"main" #1 prio=5 os_prio=0 cpu=338.57ms elapsed=91.48s allocated=14953K defined_classes=1526 tid=0x00007f10c0013af0 nid=0x2a95 in Object.wait() [0x00007f10c7d34000]
-
"main"
線程名字 -
#1
線程序號(hào) -
prio
優(yōu)先級(jí) -
os_prio
os線程優(yōu)先級(jí) -
cpu
線程獲得cpu的時(shí)間 -
elapsed
線程啟動(dòng)后經(jīng)過(guò)的wall clock time -
allocated
分配的內(nèi)存字節(jié)數(shù) -
defined_classes
線程定義的類(lèi)個(gè)數(shù) -
tid
線程id -
nid
os線程id -
in Object.wait()
表示當(dāng)前線程狀態(tài) -
[0x00007f10c7d34000]
最新java堆棧指針sp
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(java.base@17.0.9/Native Method)
- waiting on <0x000000071e77a5b0> (a java.lang.Thread)
at java.lang.Thread.join(java.base@17.0.9/Thread.java:1313)
- locked <0x000000071e77a5b0> (a java.lang.Thread)
at java.lang.Thread.join(java.base@17.0.9/Thread.java:1381)
at org.example.DeadlockDemo.main(DeadlockDemo.java:43)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@17.0.9/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@17.0.9/NativeMethodAccessorImpl.java:77)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@17.0.9/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@17.0.9/Method.java:568)
at com.sun.tools.javac.launcher.Main.execute(jdk.compiler@17.0.9/Main.java:419)
at com.sun.tools.javac.launcher.Main.run(jdk.compiler@17.0.9/Main.java:192)
at com.sun.tools.javac.launcher.Main.main(jdk.compiler@17.0.9/Main.java:132)
后面跟著的是堆棧的詳細(xì)信息,表示當(dāng)前main線程狀態(tài)是WAITING
坯辩,表示正在執(zhí)行Thread.join
中馁龟,還在等待其他線程執(zhí)行完。Locked ownable synchronizers:
代表線程擁有的排它鎖對(duì)象濒翻,例如ReentrantReadWriteLock.writeLock
。
"VM Thread" os_prio=0 cpu=15.08ms elapsed=91.47s tid=0x00007f10c01724e0 nid=0x2a9b runnable
"GC Thread#0" os_prio=0 cpu=0.14ms elapsed=91.48s tid=0x00007f10c007be30 nid=0x2a96 runnable
"G1 Main Marker" os_prio=0 cpu=0.13ms elapsed=91.48s tid=0x00007f10c008c3e0 nid=0x2a97 runnable
"G1 Conc#0" os_prio=0 cpu=0.08ms elapsed=91.48s tid=0x00007f10c008d350 nid=0x2a98 runnable
"G1 Refine#0" os_prio=0 cpu=0.14ms elapsed=91.48s tid=0x00007f10c0144bc0 nid=0x2a99 runnable
"G1 Service" os_prio=0 cpu=16.20ms elapsed=91.48s tid=0x00007f10c0145ac0 nid=0x2a9a runnable
"VM Periodic Task Thread" os_prio=0 cpu=59.59ms elapsed=91.45s tid=0x00007f10c01c6d80 nid=0x2aa6 waiting on condition
JNI global refs: 9, weak refs: 0
后面跟著的是jvm的線程信息。
Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x00007f1008001120 (object 0x000000071e77a520, a java.lang.Object),
which is held by "Thread-1"
"Thread-1":
waiting to lock monitor 0x00007f0ffc000f60 (object 0x000000071e77a510, a java.lang.Object),
which is held by "Thread-0"
Java stack information for the threads listed above:
===================================================
"Thread-0":
at org.example.DeadlockDemo.lambda$main$0(DeadlockDemo.java:19)
- waiting to lock <0x000000071e77a520> (a java.lang.Object)
- locked <0x000000071e77a510> (a java.lang.Object)
at org.example.DeadlockDemo$$Lambda$198/0x00007f1044140208.run(Unknown Source)
at java.lang.Thread.run(java.base@17.0.9/Thread.java:840)
"Thread-1":
at org.example.DeadlockDemo.lambda$main$1(DeadlockDemo.java:33)
- waiting to lock <0x000000071e77a510> (a java.lang.Object)
- locked <0x000000071e77a520> (a java.lang.Object)
at org.example.DeadlockDemo$$Lambda$199/0x00007f1044140430.run(Unknown Source)
at java.lang.Thread.run(java.base@17.0.9/Thread.java:840)
Found 1 deadlock.
在堆棧信息的最后有送,jstack已經(jīng)幫我們找到了死鎖的原因淌喻,可以看到,thread-0鎖住了<0x000000071e77a510>
對(duì)象雀摘,正在等待<0x000000071e77a520>
對(duì)象的釋放裸删,thread-1則相反。
jstack分析死循環(huán)
通過(guò)top找到占用大的java進(jìn)程然后通過(guò)top -Hp pid
找到占用異常的線程id21484
阵赠,通過(guò)printf "%x\n"
轉(zhuǎn)換位16進(jìn)制的pid涯塔,再去jstack輸出找到對(duì)應(yīng)堆棧執(zhí)行位置進(jìn)行分析jstack <pid> | grep -A 20 '<nid>'
引用資料:
[1] JDK14性能管理工具:jstack使用介紹
[2] 原來(lái)jdk自帶了這么好玩的工具 —— 使用 jstack定位死循環(huán)