jstack 命令是 Java 虚拟机(JVM)自带的诊断工具之一,主要用于生成 JVM 当前时刻的线程快照(Thread Dump) 。
线程快照包含了虚拟机中每一条线程正在执行的方法堆栈的集合。生成线程快照的主要目的是帮助定位线程出现长时间停顿的原因,例如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。
📌 jstack 命令的基本语法
最基本的用法是指定一个运行中的 Java 进程 ID(PID):
jstack [options] <pid>
或者分析一个 core dump 文件:
jstack [options] <executable> <core file>
✨ 常用参数选项详解
jstack 命令提供了一些非常有用的参数,可以帮助你获取更详细或在特殊情况下强制获取线程信息:
| 参数 | 描述 | 作用和应用场景 |
|---|---|---|
| 无选项 | 默认输出 | 打印指定 Java 进程的线程堆栈信息。 |
-l | 长格式(long listing) | 最常用的选项之一。 除了堆栈信息外,还会显示关于锁(java.util.concurrent 相关的 OwnableSynchronizer)的附加详细信息,如锁的持有者和等待者。这对于诊断死锁和与锁相关的性能问题至关重要。 |
-m | 混合模式(mixed mode) | 打印 Java 帧和 本地 C/C++ 帧的混合信息。当 Java 代码通过 JNI 调用了本地(Native)方法时,这个选项可以帮助你了解 Java 代码与本地代码之间的交互情况。 |
-F | 强制模式(force) | 当目标 Java 进程不响应(例如进程已挂起或处于某种僵死状态)时,强制输出线程堆栈。在使用时应格外小心,因为强制打印可能会对目标进程产生影响。 |
-h 或 -help | 帮助信息 | 打印 jstack 命令的帮助信息。 |
示例
-
诊断死锁(推荐):(
<pid>Java 进程 ID)jstack -l <pid> -
目标进程无响应时:
jstack -F <pid>
💡 线程快照中需要关注的状态
在 jstack 输出的线程快照中,你需要重点关注 Java 线程的几种状态:
| 线程状态 | 描述 | 关注点/含义 |
|---|---|---|
RUNNABLE | 线程正在执行或在等待 CPU 调度。 | 如果一个线程长时间处于此状态且 CPU 占用率高,可能存在死循环。 |
BLOCKED | 线程在等待进入同步区域(即等待获取 监视器锁/内置锁)。 | 输出中通常显示为 waiting for monitor entry。这是分析锁竞争的重点。 |
WAITING | 线程无限期地等待另一个线程的特定操作(如 Object.wait()、Thread.join()、LockSupport.park())。 | 输出中可能显示为 parking 或 in Object.wait() 。需要找到唤醒它的线程或操作。 |
TIMED_WAITING | 线程有时限地等待另一个线程的特定操作(如 Thread.sleep()、Object.wait(timeout)、LockSupport.parkNanos())。 | 通常是正常的,但在分析性能问题时,如果大量线程长时间处于这种状态,可能意味着请求外部资源(如网络IO)耗时过长。 |
重点关注的输出信息
waiting for monitor entry:线程正在等待获取一个对象的内置锁(moniter),通常是进入一个同步块或同步方法时。waiting on condition或parking:线程在等待一个条件或资源,例如使用了java.util.concurrent包中的锁或条件变量。Locked xxx:线程已经持有的锁。在-l模式下可以更清晰地看到。