jstack
是 Java 提供的用于生成当前 Java 进程或线程的线程快照(Thread Dump)的工具。线程快照可以帮助开发人员分析线程的执行状态,特别是在排查线程死锁、线程阻塞、性能问题和其他与线程相关的异常时非常有用。
基本用法
jstack [options] <pid>
pid
:Java 进程的进程号,可以通过jps
、ps
或其他工具获取。
常用选项
-
生成线程快照(Thread Dump)
jstack <pid>
这是最常用的命令,它会显示指定 Java 进程中所有线程的当前堆栈信息,包含每个线程的状态(如
RUNNABLE
、WAITING
、BLOCKED
等),以及它们正在执行的代码位置。输出示例:
2024-10-04 10:15:00 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.141-b15 mixed mode): "Attach Listener" #5 daemon prio=9 os_prio=0 tid=0x00007fba2c01e000 nid=0x23d5 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Service Thread" #4 daemon prio=9 os_prio=0 tid=0x00007fba2c01c000 nid=0x23d4 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #3 daemon prio=9 os_prio=0 tid=0x00007fba2c018000 nid=0x23d3 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE ...
每个线程的堆栈信息从线程名、优先级、线程状态等方面展示。
-
强制输出线程快照(
-F
)
当 JVM 没有响应时,可以使用-F
选项强制生成线程快照。在系统资源紧张或者死锁的情况下,通常 JVM 会陷入停滞,此时可以通过jstack -F
进行线程的强制快照。jstack -F <pid>
-
输出锁信息(
-l
)
输出更多与锁相关的信息,帮助分析线程持有的锁和等待锁的情况。在排查死锁和锁争用问题时非常有用。jstack -l <pid>
输出示例:
"main" #1 prio=5 os_prio=0 tid=0x000000000222e800 nid=0x1 runnable [0x00007fffa5bfe000] java.lang.Thread.State: RUNNABLE at java.io.FileInputStream.readBytes(Native Method) - locked <0x00000000fd1f5b08> (a java.io.FileInputStream) at java.io.FileInputStream.read(FileInputStream.java:255) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) - locked <0x00000000fd1f5a88> (a java.io.InputStreamReader)
在该输出中,
locked
关键字用于显示线程当前持有的锁。 -
打印 Java 和本地(C/C++)线程的混合堆栈(
-m
)
该选项会同时显示 Java 线程栈和本地(C/C++)线程栈,特别适合用来分析 JNI 调用问题和线程与操作系统原生线程的交互。jstack -m <pid>
输出示例:
"main" #1 prio=5 os_prio=0 tid=0x0000000001c8f000 nid=0x1f7f runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE at java.io.FileInputStream.readBytes(Native Method) at java.io.FileInputStream.read(FileInputStream.java:255) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) - locked <0x00000000fd1f5b08> (a java.io.FileInputStream) Native frames: (J=compiled Java code, j=interpreted, Vv=VM code) J 314 C2 java.lang.StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder; (85 bytes) @ 0x00007f4a6a0f1a8c [0x00007f4a6a0f1900+0x18c]
在这个例子中,除了 Java 堆栈信息外,还显示了本地 JNI 函数(如
Native Method
)。
常见问题分析
-
线程阻塞(BLOCKED)
当多个线程同时尝试获取同一个锁时,其中一些线程可能会进入BLOCKED
状态,等待锁的释放。通过jstack -l
,你可以查看这些线程是否因为锁争用问题而阻塞,哪些线程持有锁,哪些线程在等待锁。输出示例:
"Thread-2" #10 prio=5 os_prio=0 tid=0x00007fba4c018000 nid=0x23d6 waiting for monitor entry [0x0000000000000000] java.lang.Thread.State: BLOCKED (on object monitor) at com.example.Class.method(Class.java:50) - waiting to lock <0x00000000fd1f5b08> (a java.lang.Object) at com.example.Class.run(Class.java:100)
在这个例子中,线程
Thread-2
正在等待获取锁0x00000000fd1f5b08
。 -
死锁(Deadlock)
当多个线程相互等待彼此持有的锁时,会发生死锁。这种情况下,所有涉及的线程都会进入BLOCKED
状态,并永远无法执行下去。使用jstack -l
可以检测和诊断死锁。死锁示例:
Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x0000000000000002 (object 0x00000000fd1f5b10, a java.lang.Object), which is held by "Thread-2" "Thread-2": waiting to lock monitor 0x0000000000000001 (object 0x00000000fd1f5b08, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: "Thread-1": at com.example.Class.methodA(Class.java:20) - waiting to lock <0x00000000fd1f5b10> (a java.lang.Object) - locked <0x00000000fd1f5b08> (a java.lang.Object) "Thread-2": at com.example.Class.methodB(Class.java:30) - waiting to lock <0x00000000fd1f5b08> (a java.lang.Object) - locked <0x00000000fd1f5b10> (a java.lang.Object)
在此示例中,
Thread-1
和Thread-2
正在互相等待彼此持有的锁,导致了死锁。 -
CPU 占用过高的线程分析
当某个 Java 进程占用大量 CPU 资源时,可以通过jstack
查看所有线程的状态,并根据线程的执行代码查找可能的原因。通常,RUNNABLE
状态的线程可能是导致 CPU 使用率高的原因。分析步骤:
- 使用
top
或ps
找到占用 CPU 较高的线程的nid
(线程 ID)。 - 将
nid
转换为十六进制格式,使用jstack
查看该线程的执行堆栈。 - 结合堆栈信息和线程的状态,找到高 CPU 占用的根本原因。
- 使用
-
长时间等待的线程分析
某些线程可能长时间处于WAITING
或TIMED_WAITING
状态,可能是因为等待某个条件(如网络 I/O 或线程通信)。通过jstack
,可以查看这些线程的堆栈位置,排查是否存在问题。输出示例:
"Thread-3" #11 prio=5 os_prio=0 tid=0x00007fba4c01a000 nid=0x23d7 waiting on condition [0x0000000000000000] java.lang.Thread.State: WAITING