jstack
是用于生成 Java 进程线程快照(thread dump)的工具,通常用于分析线程状态、排查死锁、线程阻塞、CPU 占用高等问题。线程快照可以显示 JVM 内部所有线程的执行情况,包括线程的调用栈、状态等。以下是 jstack
的详细使用指南:
基本用法
jstack [options] <pid>
pid
: Java 进程的进程号。可以通过jps
、ps
或者其他系统工具查看。
常用选项
-
-F
当普通jstack
命令无法连接到 JVM 时强制生成线程快照。使用该选项可能会终止目标 JVM。jstack -F <pid>
-
-l
显示锁的附加信息,包括当前线程持有的锁以及等待的锁。jstack -l <pid>
-
-m
显示混合模式的线程堆栈,即 Java 调用栈和本地 C/C++ 代码栈。如果你怀疑本地方法(如 JNI 调用)可能导致了问题,可以使用此选项。jstack -m <pid>
-
-h
显示帮助信息。
典型输出分析
jstack
的输出显示了每个线程的状态、调用栈及相关信息。典型的线程快照格式如下:
"main" #1 prio=5 os_prio=0 tid=0x00007fabc400b000 nid=0x5a6f runnable [0x00007fab5bffb000]
java.lang.Thread.State: RUNNABLE
at com.example.MyClass.myMethod(MyClass.java:123)
at java.lang.Thread.run(Thread.java:748)
- 线程名称:
main
是线程名称,#1
是线程 ID,prio=5
是线程的优先级。 os_prio
和tid/nid
:os_prio
是操作系统中的线程优先级,tid
是 Java 层的线程 ID,nid
是操作系统层面的线程 ID。- 线程状态: 线程状态可以是
RUNNABLE
(可运行的)、WAITING
(等待状态)、TIMED_WAITING
(超时等待)或BLOCKED
(被其他线程阻塞)。 - 调用栈: 显示当前线程的调用栈,通常从 Java 方法开始。如果线程正在执行本地代码,也可能显示本地方法。
常见线程状态
-
RUNNABLE
线程在运行或等待 CPU 时间片。高 CPU 占用时,多个RUNNABLE
线程可能正在竞争资源。 -
WAITING (on object monitor)
线程在等待某个对象的锁或条件变量的通知(通过Object.wait()
)。这种线程通常在等待其他线程释放资源。 -
TIMED_WAITING (on object monitor)
与WAITING
类似,但等待是有超时时间的(如Thread.sleep()
或Object.wait(timeout)
)。 -
BLOCKED (on object monitor)
线程在试图进入同步代码块时被阻塞,等待获取一个对象的锁。 -
TIMED_WAITING (sleeping)
线程正在睡眠(通过Thread.sleep()
)。
常见场景下的 jstack
使用
1. 高 CPU 占用问题
当某个 Java 进程的 CPU 使用率异常高时,可以使用 jstack
生成线程快照,查看哪些线程处于 RUNNABLE
状态并消耗 CPU 资源。
步骤:
- 通过
top
或ps
命令找到高 CPU 占用的 Java 进程 PID。 - 执行
jstack
命令:jstack -l <pid> > thread_dump.txt
- 在输出中寻找处于
RUNNABLE
状态的线程,分析它们的调用栈,找到导致高 CPU 的原因。
2. 死锁问题
当怀疑出现死锁时,使用 jstack
可以直接检测死锁并输出相关信息。线程快照会显示哪些线程持有锁并等待其他线程释放锁。
jstack -l <pid> > thread_dump.txt
输出示例:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock Monitor@0x00000000d611c5c8, which is held by "Thread-2"
"Thread-2":
waiting to lock Monitor@0x00000000d611c3c8, which is held by "Thread-1"
上面的输出表明线程 1 和线程 2 出现了循环依赖,导致死锁。
3. 线程阻塞或响应变慢
当某个 Java 应用程序响应变慢或卡住时,使用 jstack
可以查看线程是否处于 BLOCKED
或 WAITING
状态,分析阻塞的原因。
步骤:
- 执行
jstack
,将输出保存到文件:jstack -l <pid> > thread_dump.txt
- 查找
BLOCKED
或WAITING
状态的线程,分析调用栈,查明阻塞源头。
4. Full GC 问题
当频繁发生 Full GC 时,应用程序可能会长时间停顿,使用 jstack
可以查看 Full GC 期间线程的状态。
自动获取线程快照
可以设置脚本在某些条件下(如 CPU 使用率超过某个阈值)自动生成 jstack
,例如:
#!/bin/bash
pid=$1
cpu_usage=$(top -b -n1 -p $pid | grep $pid | awk '{print $9}')
if (( $(echo "$cpu_usage > 80.0" |bc -l) )); then
jstack -l $pid > /tmp/thread_dump_$pid.txt
fi
这个脚本可以每隔几秒运行一次,当某个 Java 进程的 CPU 使用率超过 80% 时自动生成线程快照。
jstack
的限制
- 内存使用高时可能无法生成快照:当 JVM 内存使用率过高,生成线程快照可能会失败。
- 强制生成线程快照可能影响 JVM 性能:使用
-F
选项强制生成线程快照时,可能导致 JVM 暂时挂起,甚至崩溃。
通过 jstack
工具,可以较为详细地分析 Java 应用程序中线程的状态,定位 CPU 占用、死锁、线程阻塞等问题,是 Java 性能调优和问题排查的必备工具。