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进程的线程堆栈跟踪,我们通常用jstack来分析死锁和死循环等场景。
使用方式及参数
Usage:
jstack [-l][-e]
(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进程id——pid,jstack pid
就能打印出堆栈信息。-l
参数可以打印锁的信息,-e
可以打印线程的额外信息。
jstack分析死锁
我们写一个死锁的demo DeadlockDemo.java
并且运行:
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();
// 等待两个线程执行完毕
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
命令行执行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):
第一部分是虚拟机信息,SMR全称是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
线程序号prio
优先级os_prio
os线程优先级cpu
线程获得cpu的时间elapsed
线程启动后经过的wall clock timeallocated
分配的内存字节数defined_classes
线程定义的类个数tid
线程idnid
os线程idin Object.wait()
表示当前线程状态[0x00007f10c7d34000]
最新java堆栈指针sp
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
- waiting on <0x000000071e77a5b0> (a java.lang.Thread)
at java.lang.Thread.join([email protected]/Thread.java:1313)
- locked <0x000000071e77a5b0> (a java.lang.Thread)
at java.lang.Thread.join([email protected]/Thread.java:1381)
at org.example.DeadlockDemo.main(DeadlockDemo.java:43)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0([email protected]/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke([email protected]/NativeMethodAccessorImpl.java:77)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke([email protected]/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke([email protected]/Method.java:568)
at com.sun.tools.javac.launcher.Main.execute([email protected]/Main.java:419)
at com.sun.tools.javac.launcher.Main.run([email protected]/Main.java:192)
at com.sun.tools.javac.launcher.Main.main([email protected]/Main.java:132)
后面跟着的是堆栈的详细信息,表示当前main线程状态是WAITING
,表示正在执行Thread.join
中,还在等待其他线程执行完。Locked ownable synchronizers:
代表线程拥有的排它锁对象,例如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([email protected]/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([email protected]/Thread.java:840)
Found 1 deadlock.
在堆栈信息的最后,jstack已经帮我们找到了死锁的原因,可以看到,thread-0锁住了<0x000000071e77a510>
对象,正在等待<0x000000071e77a520>
对象的释放,thread-1则相反。
jstack分析死循环
通过top找到占用大的java进程然后通过top -Hp pid
找到占用异常的线程id21484
,通过转换位16进制的pid,再去jstack输出找到对应堆栈执行位置进行分析。
引用资料:
[1] JDK14性能管理工具:jstack使用介绍
[2] 原来jdk自带了这么好玩的工具 —— 使用 jstack定位死循环