一、jstack
的基本用法
1. 生成线程转储文件
jstack <pid> > threaddump.txt
- 是 Java 进程的 PID。
- threaddump.txt 是生成的线程转储文件。
2. 查看线程转储文件
打开 threaddump.txt 文件,可以查看所有线程的状态和调用栈信息。
二、jstack 的常用选项
1. 强制生成线程转储文件
如果 JVM 进程没有响应,可以使用 -F
选项强制生成线程转储文件:
jstack -F <pid> > threaddump.txt
2. 生成混合模式线程转储文件
使用-m
选项可以生成混合模式线程转储文件,包含 Java 和本地方法调用栈:
jstack -m <pid> > threaddump.txt
3. 生成锁信息
使用 -l
选项可以生成包含锁信息的线程转储文件:
jstack -l <pid> > threaddump.txt
三、线程转储文件的结构
线程转储文件通常包含以下部分:
1. 线程状态
每个线程的状态可能是以下几种之一:
RUNNABLE
:线程正在运行。BLOCKED
:线程被阻塞,等待获取锁。WAITING
:线程正在等待其他线程的通知。TIMED_WAITING
:线程正在等待,但有超时限制。TERMINATED
:线程已终止。
2. 线程调用栈
每个线程的调用栈信息,显示了线程当前执行的方法和调用链。
3. 锁信息
如果使用了 -l
选项,线程转储文件会包含锁的持有者和等待者信息。
四、使用 jstack 分析问题
1. 分析死锁
在线程转储文件中,查找 BLOCKED
状态的线程,并检查它们等待的锁。如果多个线程互相等待对方持有的锁,则可能存在死锁。
示例:
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f8b3810f800 nid=0x1e3 waiting for monitor entry [0x00007f8b2c7f0000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.DeadlockExample.methodB(DeadlockExample.java:20)
- waiting to lock <0x000000076b9c8c98> (a java.lang.Object)
at com.example.DeadlockExample.methodA(DeadlockExample.java:15)
- locked <0x000000076b9c8c88> (a java.lang.Object)
"Thread-2" #13 prio=5 os_prio=0 tid=0x00007f8b38110800 nid=0x1e4 waiting for monitor entry [0x00007f8b2c6ef000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.DeadlockExample.methodA(DeadlockExample.java:15)
- waiting to lock <0x000000076b9c8c88> (a java.lang.Object)
at com.example.DeadlockExample.methodB(DeadlockExample.java:20)
- locked <0x000000076b9c8c98> (a java.lang.Object)
2. 分析 CPU 占用过高
在线程转储文件中,查找 RUNNABLE
状态的线程,并检查它们的调用栈,找出占用 CPU 的代码。
示例:
"Thread-0" #11 prio=5 os_prio=0 tid=0x00007f8b3810e800 nid=0x1e2 runnable [0x00007f8b2c8f1000]
java.lang.Thread.State: RUNNABLE
at com.example.CPUIntensiveTask.run(CPUIntensiveTask.java:10)
3. 分析线程阻塞
在线程转储文件中,查找 WAITING
或 TIMED_WAITING
状态的线程,并检查它们等待的条件或锁。
示例:
"Thread-3" #14 prio=5 os_prio=0 tid=0x00007f8b38111800 nid=0x1e5 waiting on condition [0x00007f8b2c5ee000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076b9c8ca8> (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 com.example.BlockingExample.run(BlockingExample.java:15)
五、结合其他工具使用 jstack
1. 使用 top
找到高 CPU 线程
top -H -p <pid>
- -H:显示线程级别的 CPU 使用情况。
- 是 Java 进程的 PID。
2. 将线程 ID 转换为十六进制
找到占用 CPU 最高的线程,将其线程 ID 转换为十六进制:
printf "%x\n" <thread_id>
3. 在 jstack 中查找线程
生成线程转储文件,并在文件中查找对应的线程 ID:
jstack <pid> > threaddump.txt
grep <thread_id_in_hex> threaddump.txt
六、总结
jstack 是分析 JVM 线程问题的重要工具,可以帮助定位死锁、线程阻塞、CPU 占用过高等问题。通过结合 top 和其他工具,可以更高效地分析线程状态和调用栈。