介绍
jstack(Java Stack Trace)是 JDK 自带的命令行工具,无需额外安装,核心作用是:
- 导出指定 Java 进程的线程堆栈信息(包括线程状态、调用栈、锁持有情况等);
- 排查线程相关问题:死锁、线程阻塞、死循环、线程卡死、接口响应慢等;
- 适用于所有 Java 应用(Web 工程、微服务、桌面程序等),支持 Windows/Linux/Mac 全平台。
核心优势:轻量、无侵入、输出信息精准,是定位线程问题的“终极工具”(比图形化工具更深入)。
安装与环境准备
1. 安装
jstack 是 JDK 内置工具,只要安装了 JDK(版本≥1.5)就自带:
- 检查 JDK 安装:打开 cmd(Windows)/终端(Linux),执行
java -version,能显示版本号即安装成功; - 找到 jstack 路径:JDK 安装目录下的
bin文件夹(比如 Windows 下C:\Program Files\Java\jdk1.8.0_301\bin\jstack.exe)。
2. 环境配置(可选,方便使用)
- Windows:将 JDK 的
bin目录添加到系统环境变量PATH,即可在任意目录执行jstack; - Linux/Mac:配置
JAVA_HOME并将$JAVA_HOME/bin加入PATH。
关键前置步骤:获取 Java 进程 PID
执行 jstack 前必须先拿到目标进程的 PID,使用 jps 命令:
# Windows/Linux/Mac 通用(JDK 自带)
jps -l
# 输出示例(PID 在前,进程名在后)
12345 org.apache.catalina.startup.Bootstrap # Tomcat 进程,PID=12345
67890 com.xxx.SpringBootApplication # Spring Boot 进程,PID=67890
- Windows 额外方式:任务管理器 → 详细信息 → 找到
java.exe/javaw.exe,查看“PID”列; - Linux 额外方式:
ps -ef | grep java。
用法
1. 基础命令格式
jstack [选项] <进程ID(PID)>
进程ID(PID):必须参数,指要排查的 Java 进程编号;
核心选项:
| 选项 | 作用 |
|---|---|
| -F | 强制导出堆栈(当进程挂起、无响应时使用) |
| -l | 输出额外信息(比如锁的详细信息,排查死锁必加) |
| -m | 输出混合堆栈(包含 Java 线程和本地方法(C/C++)堆栈,排查 JNI 问题用) |
| -h/-help | 查看帮助信息 |
使用场景
场景1:导出线程堆栈(基础用法)
# 导出 PID=12345 的线程堆栈,输出到文件(方便查看)
jstack 12345 > thread.log
# 排查死锁时,加 -l 选项(输出锁信息)
jstack -l 12345 > thread_with_lock.log
# 进程无响应时,强制导出
jstack -F 12345 > thread_force.log
- 导出的
thread.log可用记事本、Notepad++、VS Code 等工具打开查看。
场景2:排查线程死锁(高频场景)
jstack -l <PID> 会自动检测死锁,并在堆栈末尾标注死锁信息,示例:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0000000016b4e668 (object 0x000000076b0081c0, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0000000016b4c198 (object 0x000000076b0081d0, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.xxx.DeadLockDemo.run(DeadLockDemo.java:25)
- waiting to lock <0x000000076b0081c0> (a java.lang.Object)
- locked <0x000000076b0081d0> (a java.lang.Object)
"Thread-0":
at com.xxx.DeadLockDemo.run(DeadLockDemo.java:15)
- waiting to lock <0x000000076b0081d0> (a java.lang.Object)
- locked <0x000000076b0081c0> (a java.lang.Object)
Found 1 deadlock.
- 关键信息:死锁线程名、锁的对象、卡死的代码行(
DeadLockDemo.java:25),可直接定位死锁代码。
场景3:排查线程卡死/阻塞(比如 Timer 任务耗时过长)
在导出的堆栈中搜索线程名(比如 Timer-2),查看线程状态和调用栈:
# 正常的 Timer 线程(TIMED_WAITING,等待执行)
"Timer-2" #20 daemon prio=5 os_prio=0 tid=0x000000001e8a8000 nid=0x4f4 waiting on condition [0x000000001f7ef000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076b0081c0> (a java.util.TaskQueue)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.TimerThread.mainLoop(Timer.java:552)
at java.util.TimerThread.run(Timer.java:505)
# 卡死的 Timer 线程(RUNNABLE,卡在业务方法)
"Timer-2" #20 daemon prio=5 os_prio=0 tid=0x000000001e8a8000 nid=0x4f4 RUNNABLE [0x000000001f7ef000]
java.lang.Thread.State: RUNNABLE
at com.xxx.MyTask.doBusiness(MyTask.java:25) # 卡死在业务方法第25行
at com.xxx.MyTask.run(MyTask.java:15)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
- 关键判断:
- 线程状态为
RUNNABLE但长时间停在某个业务方法 → 方法执行卡死(死循环/耗时操作); - 线程状态为
BLOCKED→ 等待获取锁,可能是锁竞争导致阻塞。
- 线程状态为
场景4:排查 CPU 100% 问题
- 用
top(Linux)/任务管理器(Windows)找到占用 CPU 最高的 Java 线程 ID(注意是线程ID,不是进程ID); - 将线程 ID 转为十六进制(比如 1234 → 0x4D2);
- 用
jstack <进程PID>导出堆栈,搜索十六进制线程 ID,定位对应的线程和方法:"Thread-10" #10 prio=5 os_prio=0 tid=0x000000001e8a8000 nid=0x4d2 RUNNABLE [0x000000001f7ef000] java.lang.Thread.State: RUNNABLE at com.xxx.CpuHighDemo.loop(CpuHighDemo.java:10) # 死循环导致CPU 100%
线程状态解读
jstack 输出的线程状态是排查问题的关键,常见状态及含义:
| 线程状态 | 含义 | 是否正常 | 排查方向 |
|---|---|---|---|
| RUNNABLE | 运行中(执行代码逻辑) | 正常/异常 | 长时间停留→死循环/耗时操作 |
| TIMED_WAITING | 定时等待(比如 Thread.sleep()、Timer 等待执行) | 正常 | 无(这是线程休眠的正常状态) |
| WAITING | 等待(比如 Object.wait()、LockSupport.park()) | 正常 | 无(等待唤醒,非阻塞) |
| BLOCKED | 阻塞(等待获取锁) | 异常 | 锁竞争、死锁 |
| TERMINATED | 终止(线程已结束) | 异常 | 线程被未捕获异常杀死 |
jstack 优缺点
优点
- 输出信息精准,能直接定位到代码行;
- 无侵入,执行命令不影响程序运行;
- 支持强制导出(进程无响应时仍可用);
- 跨平台,Windows/Linux/Mac 用法一致。
缺点
- 命令行工具,需要手动分析堆栈内容(对新手不友好);
- 只能导出“某一时刻”的堆栈,无法监控线程状态变化;
- 输出内容可能较多,需要筛选关键信息。
总结
jstack是 JDK 内置的命令行线程排查工具,核心用于导出线程堆栈,定位死锁、线程卡死、阻塞、CPU 高占比等问题;- 核心用法:
jps -l找 PID →jstack -l <PID> > thread.log导出堆栈 → 分析线程状态和调用栈; - 关键技巧:
- 排查死锁加
-l选项; - 进程无响应加
-F选项; - 搜索线程名/十六进制线程 ID 快速定位问题线程;
- 排查死锁加
- 配套使用:jstack 适合“静态排查”(某一时刻的问题),搭配 JConsole/VisualVM 可实现“动态监控+静态分析”。
简单说,jstack 是 Java 线程问题排查的“终极武器”,只要是线程相关的问题,几乎都能通过它找到根因。