jstack (最常用、最标准)
jstack 是 JDK 自带的工具,专门用于生成 Java 虚拟机当前时刻的线程快照,这是分析线程问题(如死锁、CPU 100%)最核心、最可靠的方法。

找到 Java 进程的 PID
你需要知道你要分析的 Java 进程的进程 ID (PID)。
# 方法1: 使用 ps 命令 ps -ef | grep java # 方法2: 使用 jps 命令 (推荐,只显示 Java 进程) jps -l
假设你找到的 PID 是 12345。
使用 jstack 生成线程快照
# 基本用法 jstack <PID> > thread_dump.txt # 示例 jstack 12345 > thread_dump_12345.txt
这会在当前目录下生成一个 thread_dump.txt 文件,里面包含了该 JVM 中所有线程的详细信息。
分析 jstack 输出
jstack 的输出非常丰富,关键信息如下:

- 线程状态:每个线程都有一个状态,如
RUNNABLE(运行中)、WAITING(等待)、TIMED_WAITING(定时等待)、BLOCKED(阻塞)。 - 线程名:
"main","http-nio-8080-exec-1"等。 - 线程 ID:
nid,即 Native ID,对应到 Linux 线程的 LWP ID。 - 栈跟踪:最关键的部分,显示了线程执行到的方法调用路径,帮你定位问题代码。
示例分析:
# 假设我们用 jstack 生成的文件内容如下 (部分摘录)
"main" #1 prio=5 os_prio=0 tid=0x00007f8c3400b800 nid=0x5a03 waiting on condition [0x00007f8c5a2fe000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d1bf3ef0> (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:2032)
at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
at com.example.MyMain.run(MyMain.java:25)
at java.lang.Thread.run(Thread.java:748)
"http-nio-8080-exec-1" #12 prio=5 os_prio=0 tid=0x00007f8c34012800 nid=0x5a1c runnable [0x00007f8c5a0fd000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x00000000d1bf5c50> (a java.lang.Object)
at java.io.InputStreamReader.read(InputStreamReader.java:181)
at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:226)
... (更多框架调用)
解读:
- 线程
main:- 状态是
WAITING。 - 它在
ArrayBlockingQueue.take()方法上等待,说明它在从一个队列中获取数据,如果队列为空,它就会一直等待,这是正常的行为。
- 状态是
- 线程
http-nio-8080-exec-1:- 状态是
RUNNABLE,表示它正在运行。 - 它的栈顶是
java.net.SocketInputStream.socketRead0,说明它正在等待从网络 socket 读取数据,这也是一个正常的工作线程状态。
- 状态是
查找问题:
- 查找死锁:
jstack会自动检测死锁,并在输出文件的开头明确报告。"Found one Java-level deadlock:"
- 查找 CPU 高占用:找到状态为
RUNNABLE的线程,观察其栈跟踪,如果栈顶总是在一个循环或计算密集的方法中(MyHeavyComputation.doCalc()),那这个线程很可能就是导致 CPU 100% 的元凶。
top / htop + H (实时监控)
如果你只是想快速看一下哪个 Java 线程占用了大量 CPU,而不需要完整的栈跟踪,这个方法非常高效。

使用 top 命令
top -p <PID>
top -p 12345。
- 按
H键,top会切换到线程视图。 - 你会看到进程下的所有线程,找到
CPU使用率最高的那个线程。 - 记下它的
ID(PID 列下的值,这个是 Linux 线程 ID,也叫 LWP)。
将线程 ID 转换为十六进制
jstack 输出中的 nid 是十六进制的 Linux 线程 ID,所以你需要将上一步得到的十进制 ID 转换为十六进制。
# 假设 top 中看到的 CPU 占用最高的线程 ID 是 23109 printf "%x\n" 23109 # 输出可能是 5a95
在 jstack 输出中查找
打开之前用 jstack 生成的 thread_dump.txt 文件,搜索 nid=0x5a95,就能找到这个高 CPU 线程的完整调用栈。
htop 的优势:
htop 比 top 更现代、更易用。
- 启动
htop后,直接选中你的 Java 进程。 - 按
F2或H(取决于版本) 进入设置,确保 "Tree view" 和 "Hide userland threads" 是关闭的。 - 按
F5切换到树状视图。 - 再次按
F5可以展开/折叠进程,看到其下的线程。 - 直接按
H就可以显示/隐藏线程视图,找到高 CPU 线程后,直接按c可以查看其完整的命令行,比top更方便。
JConsole / VisualVM (图形化界面)
如果你更喜欢图形化工具,并且可以连接到目标 JVM,JConsole 和 VisualVM 是非常好的选择,它们通常随 JDK 一起安装。
启动工具
# 启动 VisualVM (功能更强大) jvisualvm # 或者启动 JConsole jconsole
连接到 JVM
启动后,工具会自动扫描本机所有运行的 Java 进程,双击你要连接的进程。
查看线程
连接成功后,在左侧选择 “线程” (Threads) 标签页。
- 线程监控:你可以看到所有线程的状态、CPU 时间等。
- 线程 dump:工具栏上有一个“线程 Dump”按钮,点击它就能生成当前时刻的线程快照,效果和
jstack一样,但是在图形界面里查看,更直观。 - 死锁检测:如果发生死锁,图形界面会有非常明显的提示。
优点:
- 图形化,直观易懂。
- 可以实时监控线程状态变化。
- VisualVM 还可以分析堆内存、CPU 分析等,功能非常强大。
缺点:
- 需要图形界面支持(例如通过 X11 转发,或者直接在服务器上运行桌面环境)。
- 对于生产环境,通常不允许开放图形端口,所以更常用于本地开发或测试环境。
总结与对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
jstack |
标准、强大、无图形依赖,输出详细,是分析问题的金标准。 | 需要两步操作(jps + jstack),分析文本需要一定经验。 |
生产环境问题排查(死锁、CPU飙高、线程hang住)的首选。 |
top/htop |
快速、实时,能迅速定位到高CPU的线程。 | 无法直接看到线程调用栈,需要配合 jstack 深入分析。 |
快速初步定位CPU问题,缩小排查范围。 |
| JConsole/VisualVM | 图形化界面,直观,可实时监控,功能全面(内存、CPU等)。 | 需要图形环境,对生产环境不友好,可能影响性能。 | 本地开发调试、测试环境的日常监控和问题分析。 |
推荐工作流程:
- 发现异常:通过
top、htop或监控平台发现某个 Java 进程 CPU 占用很高。 - 定位线程:使用
top -H或htop找到占用 CPU 最高的那个 Linux 线程 ID (LWP)。 - 转换并分析:将该 LWP ID 转为十六进制,然后用
jstack <PID> > dump.txt生成快照,在文件中搜索nid=<十六进制ID>,查看其调用栈,定位问题代码。 - 深入分析:如果问题复杂,可以使用 VisualVM 连接进行更全面的分析。
