JDK工具:jstack查看线程堆栈信息、排查问题 作者:马育民 • 2026-02-24 17:29 • 阅读:10003 # 介绍 `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` 命令: ```bash # 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. 基础命令格式 ```bash jstack [选项] <进程ID(PID)> ``` **进程ID(PID)**:必须参数,指要排查的 Java 进程编号; **核心选项**: | 选项 | 作用 | |------|----------------------------------------------------------------------| | -F | 强制导出堆栈(当进程挂起、无响应时使用)| | -l | 输出额外信息(比如锁的详细信息,排查死锁必加)| | -m | 输出混合堆栈(包含 Java 线程和本地方法(C/C++)堆栈,排查 JNI 问题用) | | -h/-help | 查看帮助信息 | # 使用场景 ### 场景1:导出线程堆栈(基础用法) ```bash # 导出 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 ` 会**自动检测死锁**,并在堆栈末尾标注死锁信息,示例: ``` 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% 问题 1. 用 `top`(Linux)/任务管理器(Windows)找到占用 CPU 最高的 Java 线程 ID(注意是线程ID,不是进程ID); 2. 将线程 ID 转为十六进制(比如 1234 → 0x4D2); 3. 用 `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 优缺点 ### 优点 1. 输出信息精准,能直接定位到代码行; 2. 无侵入,执行命令不影响程序运行; 3. 支持强制导出(进程无响应时仍可用); 4. 跨平台,Windows/Linux/Mac 用法一致。 ### 缺点 1. 命令行工具,需要手动分析堆栈内容(对新手不友好); 2. 只能导出“某一时刻”的堆栈,无法监控线程状态变化; 3. 输出内容可能较多,需要筛选关键信息。 # 总结 1. `jstack` 是 JDK 内置的**命令行线程排查工具**,核心用于导出线程堆栈,定位死锁、线程卡死、阻塞、CPU 高占比等问题; 2. 核心用法:`jps -l` 找 PID → `jstack -l > thread.log` 导出堆栈 → 分析线程状态和调用栈; 3. 关键技巧: - 排查死锁加 `-l` 选项; - 进程无响应加 `-F` 选项; - 搜索线程名/十六进制线程 ID 快速定位问题线程; 4. 配套使用:jstack 适合“静态排查”(某一时刻的问题),搭配 JConsole/VisualVM 可实现“动态监控+静态分析”。 简单说,`jstack` 是 Java 线程问题排查的“终极武器”,只要是线程相关的问题,几乎都能通过它找到根因。 原文出处:http://malaoshi.top/show_1GW2pxQXv5vL.html