jstack用于查看Java进程线程堆栈,基本命令为jstack ,支持-l(显示锁详情)、-e(导出本地帧)、-F(强制dump)等参数;需注意容器环境权限与挂载限制,虚拟线程需显式启用参数才可见。

怎么用 jstack 查看 Java 进程的线程堆栈
jstack 是 JDK 自带的诊断工具,直接输出目标 JVM 进程中所有线程的当前调用栈。它不依赖应用是否响应,只要进程还在运行(哪怕卡死),就能抓到快照。
基本用法:jstack ,其中 是 Java 进程的进程 ID,可通过 jps -l 或 ps aux | grep java 获取。
- 加
-l参数可显示锁详细信息(比如java.util.concurrent.locks.ReentrantLock$NonfairSync的持有/等待状态) - 加
-e(JDK 8u60+)可导出额外的本地帧(native frames),对排查 JNI 或 GC 线程阻塞有帮助 - 若进程无响应但
jstack报Unable to get pid of LinuxThreads manager thread,通常是容器环境或使用了-XX:+UseContainerSupport但未正确配置 cgroup,此时尝试加-F强制 dump(需 root 权限)
jstack 输出里怎么看线程状态和锁竞争
输出按线程分块,每块以 "Thread-0" #12 prio=5 os_prio=0 tid=0x00007f8b4c0a2000 nid=0x3a5b waiting on condition [0x00007f8b3d9f9000] 开头,关键字段含义:
-
tid:JVM 内部线程 ID(十六进制),可用于jcmd关联本地内存VM.native_memory summary -
nid:操作系统级线程 ID(十六进制),可用top -H -p查 CPU 占用 -
waiting on condition/parking to wait for/in Object.wait():说明线程在等某个条件,未必是死锁,但需结合后续栈帧看等谁 -
java.lang.Thread.State: BLOCKED (on object monitor)+waiting to lock:明确表示该线程被阻塞在 synchronized 块外,正等待获取指定对象监视器
注意:jstack 不会自动标出死锁,得靠人眼扫描多个线程是否循环等待同一组锁;也可用 jstack -l 快速定位(仅当 JVM 检测到时才输出)。
立即学习“Java免费学习笔记(深入)”;
容器环境下 jstack 失效的常见原因和绕过方法
在 Docker/Kubernetes 中直接执行 jstack 容易失败,典型现象包括:Can't attach to process、No such process、或只看到 Signal Dispatcher 等少量线程。
- 根本原因是:JDK attach mechanism 依赖
/proc/和/root /tmp可写,而容器常限制procfs挂载或禁用ptrace - 解决路径一:启动容器时加
--cap-add=SYS_PTRACE并挂载宿主机/proc(-v /proc:/proc:ro) - 解决路径二:进入容器后,用
kill -3触发java进程自己打印堆栈到 stdout/stderr(前提:JVM 启动参数含-XX:+PrintGCDetails不影响,但必须没重定向掉日志) - 解决路径三:用
jcmd替代(兼容性更好,部分受限容器仍可工作)Thread.print
为什么有时候 jstack 看不到你关心的线程
不是所有线程都会出现在 jstack 输出里。以下情况会导致“丢失”:
- 线程已退出但栈帧还残留在 native memory(如
Thread.run()执行完,但 JVM 尚未回收线程对象)——此时不会显示 - 使用
VirtualThread(Project Loom)且未开启-Djdk.virtualThreadDump=true,则jstack默认不展示虚拟线程(JDK 21+) - 某些 native 线程(如 G1 Conc Refine Thread、ZStatSampler)属于 JVM 内部调度线程,
jstack不捕获其 Java 栈,只显示Native frames(若有) - 如果应用用了
Unsafe.park()或自定义线程池且线程处于WAITING状态但未关联 monitor,jstack只能显示parking to wait for,无法指出具体等待哪个AbstractQueuedSynchronizer实例
真正难的从来不是执行 jstack,而是从几百行输出里快速定位哪几行栈帧对应业务慢点、哪几个 tid 在争同一个 0x... 锁地址——这需要反复比对、结合代码上下文,没法靠工具自动完成。








