在 Java 应用运维中,线程问题(死锁、阻塞、卡死)是导致应用卡顿、无响应的高频原因,而 jstack 就是 JDK 自带的专业线程分析工具。它能实时抓取 Java 进程的线程堆栈信息,清晰展示线程的运行状态、调用链路,帮我们快速定位死锁、线程阻塞、CPU 过高、线程泄漏等问题,与 jstat(GC 监控)、jcmd(全能诊断)、jmap(内存分析)共同构成 JVM 排查“四件套”。
这篇博客延续前几篇的简洁风格,避开复杂理论,只讲实用用法,带你快速掌握 jstack 核心命令,轻松应对各类线程相关排查场景。
一、jstack 是什么?
jstack(JVM Stack Trace)是 JDK 1.5 及以上版本自带的命令行工具,核心功能是捕获运行中 Java 进程的线程堆栈快照,展示每个线程的状态、调用栈、锁持有情况等信息,是排查线程相关问题的“核心利器”。
核心定位:专注线程分析,弥补 jstat 不支持线程监控、jcmd 线程分析不够细致、jmap 侧重内存的短板,专门解决线程卡顿、死锁、阻塞等问题。
核心优势:
精准聚焦:专门针对线程分析,输出信息简洁精准,直接定位线程问题根源
无侵入性:无需重启应用,仅抓取线程快照,性能开销极低,适合线上环境使用
功能实用:自动检测死锁,清晰展示线程状态(RUNNABLE、BLOCKED、WAITING 等),无需手动分析
跨平台兼容:适配 Windows、Linux、Mac 等所有主流系统,支持所有 JDK 1.5+ 版本,无需额外安装
二、前置条件
已安装
JDK 1.5+(jstack 从 JDK 1.5 开始引入,JRE 不包含,需确保 JDK 环境配置完整)配置好 JDK 环境变量,在命令行输入
jstack -version可正常显示版本信息拥有目标 Java 进程的访问权限(与 jstat、jcmd、jmap 权限要求一致,线上环境需注意权限管控)
若应用处于严重卡顿、无响应状态,仍可使用 jstack 抓取线程快照,无需担心工具无法执行
三、核心命令(直接复制可用)
1. 先获取 Java 进程 PID(与前三种工具一致)
# 方法1:jps 查看(最常用,统一操作)
jps -l
# 方法2:jcmd 查看(兼容前序教程)
jcmd -l
# 方法3:Linux 额外方法(按需使用)
ps aux | grep java
输出示例:
12345 com.example.Application
67890 org.apache.tomcat.startup.Bootstrap
记住目标进程 PID(如 12345),后续所有 jstack 命令均需指定该 PID。
2. 高频核心命令(日常必用,重点掌握)
(1)抓取线程堆栈快照(最基础、最常用)
# 格式:jstack <PID> [> 输出文件路径]
# 可选:输出到文件,方便后续分析(推荐,尤其线上排查)
jstack 12345 > jstack-thread.log
核心说明:
执行后会输出该进程所有线程的详细信息,包括线程 ID、状态、调用栈、锁持有情况等
输出到文件(> jstack-thread.log),可避免命令行输出过长导致信息丢失,便于后续慢慢分析
该命令无明显性能开销,线上环境可随时执行,无需担心影响业务
用途:快速获取所有线程的运行状态,初步排查线程卡顿、无响应等问题。
(2)抓取线程快照并显示锁信息(排查死锁、锁竞争)
# 格式:jstack -l <PID> [> 输出文件路径]
# -l:显示额外的锁信息,包括拥有的锁、等待的锁,自动检测死锁
jstack -l 12345 > jstack-lock.log
核心优势:这是排查死锁的“核心命令”,-l 参数会自动检测进程中的死锁,并在输出末尾标注死锁相关线程的详细信息,无需手动分析锁引用关系,大大提升排查效率。
用途:排查死锁、锁竞争、线程阻塞等问题,精准定位持有锁的线程和等待锁的线程。
(3)强制抓取线程快照(应对应用卡死场景)
# 格式:jstack -F <PID>
# -F:强制抓取,当应用严重卡死、无响应,普通 jstack 命令无法执行时使用
jstack -F 12345
注意:该命令仅在普通命令无法执行时使用,强制抓取可能会导致输出信息不够完整,优先使用普通命令和 -l 参数命令。
(4)过滤指定状态的线程(精准排查)
# 格式:jstack <PID> | grep "线程状态"(Linux/Mac)
# 示例1:过滤出 BLOCKED(阻塞)状态的线程
jstack 12345 | grep "BLOCKED"
# 示例2:过滤出 WAITING(等待)状态的线程
jstack 12345 | grep "WAITING"
用途:快速筛选出异常状态的线程(如 BLOCKED、WAITING),避免在大量线程信息中逐一查找,提升排查效率。
3. 线程状态解读(关键!必看)
jstack 输出中,线程状态是排查问题的核心依据,重点关注以下 4 种常见状态:
RUNNABLE:运行中状态,线程正在执行任务,属于正常状态(若该状态线程过多,需关注 CPU 占用)
BLOCKED:阻塞状态,线程正在等待获取锁,无法执行任务,是导致应用卡顿的主要原因之一(重点排查)
WAITING:等待状态,线程主动等待某个条件(如 wait() 方法),需结合业务代码分析是否正常
TIMED_WAITING:超时等待状态,线程等待指定时间后会自动唤醒(如 sleep() 方法),一般属于正常状态,无需过度关注
四、实战排查思路(与前三种工具互补)
- 应用卡顿、无响应(核心场景):
① 用 jstat 监控 GC 状态 → 排除 GC 频繁导致的卡顿;
② 用 jstack -l 12345 → 抓取线程快照,查看是否有大量 BLOCKED 状态线程;
③ 定位 BLOCKED 线程的调用栈 → 找到等待的锁,查看持有该锁的线程,分析锁竞争原因(如同步代码块过长、死锁)。
- 死锁排查(紧急场景):
① 执行 jstack -l 12345 → 命令会自动检测死锁,在输出末尾标注“Found one Java-level deadlock”;
② 查看死锁线程的详细信息 → 找到相互持有锁、相互等待的线程;
③ 结合业务代码 → 定位死锁产生的代码(如锁的获取顺序不一致),修改锁的获取逻辑即可解决。
- CPU 过高排查(线程相关):
① 用 top 命令(Linux)/ 任务管理器(Windows) → 找到占用 CPU 过高的 Java 进程 PID;
② 用 jstack 12345 → 抓取线程快照,结合 CPU 占用情况,找到 RUNNABLE 状态且调用栈异常的线程;
③ 分析该线程的业务代码 → 排查是否存在死循环、频繁计算等导致 CPU 过高的问题。
- 线程泄漏排查(长期运行场景):
① 定期用 jstack 抓取线程快照,保存到不同文件;
② 对比多份快照 → 查看线程数量是否持续增加,是否有大量长期处于 WAITING 状态的线程;
③ 结合 jmap 分析 → 排查线程引用是否未释放,导致线程无法被 GC 回收。
五、jstack 与 jstat、jcmd、jmap 对比(怎么选?)
jstat:侧重 GC 实时监控,轻量无侵入,仅关注 GC 频率和内存变化,不涉及线程分析。
jcmd:侧重 全能诊断,可查看线程堆栈(Thread.print 命令),但线程分析不够细致,适合快速初步诊断。
jmap:侧重 内存深度分析,专注堆内存和对象,用于排查内存泄漏、OOM 问题,与线程分析无关。
jstack:侧重 线程深度分析,专门排查死锁、线程阻塞、CPU 过高、线程泄漏等问题,是线程排查的专属工具,功能最精准。
日常搭配:jstat 监控 GC 异常 → jcmd 初步诊断 → 若怀疑线程问题,用 jstack 深入分析;若怀疑内存问题,用 jmap 深入分析,四者结合覆盖所有 JVM 排查场景。
六、注意事项
jstack 抓取的是“瞬时”线程快照,若线程问题是偶发的,可能需要多次抓取快照,对比分析线程状态变化。
线上环境建议将线程快照输出到文件,避免命令行输出过长导致信息丢失,同时便于后续存档和分析。
仅在应用严重卡死、普通 jstack 命令无法执行时,才使用
-F强制抓取,强制抓取可能导致快照信息不完整。分析线程快照时,重点关注 BLOCKED 状态的线程,结合调用栈和锁信息,快速定位问题根源,避免过度关注正常状态的线程。
JDK 1.8 及以上版本,jstack 对线程状态的识别更精准,建议使用该版本及以上 JDK,提升排查效率。
七、总结
jstack 是 JVM 线程排查的“专属神器”,专注线程堆栈分析,能快速定位死锁、线程阻塞、CPU 过高、线程泄漏等问题,是 Java 开发/运维必备的核心工具,与前三种工具互补,构成完整的 JVM 排查工具链。
日常最推荐 3 条高频命令(记牢够用):
# 1. 抓取线程快照(基础用法)
jstack <PID> > jstack.log
# 2. 抓取快照并显示锁信息(排查死锁、锁竞争,推荐)
jstack -l <PID> > jstack-lock.log
# 3. 强制抓取快照(应对应用卡死场景)
jstack -F <PID>
核心要点:jstack 无侵入、功能精准,重点关注线程状态(尤其是 BLOCKED)和锁信息,结合业务代码分析,能快速解决各类线程相关问题,是线上应用卡顿、无响应排查的首选工具。