jstack.exe 是 Java 虚拟机(JVM)自带的一种堆栈跟踪工具,它主要用于生成 Java 虚拟机当前时刻的线程快照。线程快照是当前 JVM 内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。
当 Java 程序出现长时间停顿或者无响应时,可以使用 jstack 命令来查看各个线程的调用堆栈,从而了解没有响应的线程到底在后台做什么事情,或者等待什么资源。此外,如果 Java 程序崩溃生成了 core 文件,jstack 工具也可以用来获取 core 文件的 Java stack 和 native stack 的信息,以助于诊断 Java 程序的崩溃原因和问题所在。
连接正在运行的进程:
jstack [-l] <pid>
连接挂起的进程:
jstack -F [-m] [-l] <pid>
连接核心文件:
jstack [-m] [-l] <executable> <core>
连接远程调试服务器:
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
参数说明:
option:可选参数,用于指定 jstack 命令的行为。
pid:需要打印堆栈信息的 Java 进程的进程 ID。
executable:产生 core dump 的 Java 可执行程序。
core:将被打印信息的 core dump 文件。
remote server IP or hostname:远程调试服务的主机名或 IP 地址。
server_id:唯一 ID,用于区分同一台主机上的多个远程调试服务。
命令支持如下选项:
-F 强制线程转储。当 jstack <pid> 没有响应时使用(进程被挂起)
-m,同时打印 java 和本地框架(混合模式)
-l 长列表。打印有关锁的附加信息
-h 或 -help 用于打印帮助信息
(1)打印某个进程的堆栈信息,如下:
D:\share_dir\ShareDoc> jstack 2256 2024-07-08 22:46:08 Full thread dump OpenJDK 64-Bit Server VM (17.0.6+10-b829.9 mixed mode): Threads class SMR info: _java_thread_list=0x0000018a745060e0, length=64, elements={ 0x000001899fc74940, 0x00000189c81fc660, 0x00000189c81ff760, 0x00000189c8214400, 0x00000189c8c10940, 0x00000189c8c2a840, 0x00000189c8c2d110, 0x00000189c8c31200, 0x00000189c8c323d0, 0x00000189c8c49610, 0x00000189c8d680d0, 0x00000189c97f5bd0, 0x000001899fc756c0, 0x00000189f3a82010, 0x00000189f3ab3660, 0x00000189f3ab4000, 0x00000189f39e8010, 0x00000189f39e89b0, 0x00000189f39e6800, 0x00000189f39e7b40, 0x00000189f39e1630, 0x00000189f39e24a0, 0x00000189f39e5990, 0x00000189f39e2e40, 0x0000018a212d0560, 0x0000018a684c0a20, 0x0000018a6a129310, 0x0000018a72a08220, 0x0000018a72a0a3d0, 0x0000018a72a09560, 0x0000018a72a0a8a0, 0x0000018a6fb6f6c0, 0x00000189c94e9290, 0x0000018a74558620, 0x0000018a745611b0, 0x0000018a745671f0, 0x0000018a74568530, 0x0000018a75d3e320, 0x0000018aa6056740, 0x0000018aa60558d0, 0x0000018aa6058dc0, 0x0000018aa603dca0, 0x0000018aa60459c0, 0x0000018aa603f4b0, 0x0000018aa7a90c40, 0x0000018aa7a9b980, 0x00000189f3ab27f0, 0x00000189f3a829b0, 0x0000018a7455eb30, 0x0000018a7455b170, 0x0000018a75d404d0, 0x0000018a75d33ab0, 0x0000018aa6041660, 0x0000018aa603bfc0, 0x0000018a6fb73a20, 0x0000018a6fb790c0, 0x0000018aa7a82070, 0x00000189f39e3310, 0x00000189f39e1160, 0x0000018aa7ab6aa0, 0x0000018aa7aa49e0, 0x0000018aa7ab5290, 0x0000018aa7ab7440, 0x0000018a74564b70 } "main" #1 prio=5 os_prio=0 cpu=2109.38ms elapsed=9952.72s tid=0x000001899fc74940 nid=0x2fc8 waiting on condition [0x00000025756fe000] java.lang.Thread.State: TIMED_WAITING (parking) at jdk.internal.misc.Unsafe.park(java.base@17.0.6/Native Method) - parking to wait for <0x00000000c2dc66e8> (a kotlinx.coroutines.BlockingCoroutine) at java.util.concurrent.locks.LockSupport.parkNanos(java.base@17.0.6/LockSupport.java:252) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:88) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at com.intellij.idea.Main.main(Main.kt:40) ... "G1 Refine#1" os_prio=2 cpu=0.00ms elapsed=9950.28s tid=0x00000189f31cb170 nid=0x5cec runnable "G1 Service" os_prio=2 cpu=234.38ms elapsed=9952.72s tid=0x000001899fd13e20 nid=0x3c4c runnable "VM Periodic Task Thread" os_prio=2 cpu=406.25ms elapsed=9951.76s tid=0x00000189c93df610 nid=0x4c54 waiting on condition JNI global refs: 197, weak refs: 443 JNI global refs memory usage: 2739, weak refs: 13041 JNI global refs: 197, weak refs: 443
上面命令会打印出该进程的线程堆栈信息。
(2)强制打印无响应进程的堆栈信息
D:\share_dir\ShareDoc> jstack -F 21108 Attaching to process ID 21108, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.45-b02 Deadlock Detection: No deadlocks found. Thread 37: (state = IN_NATIVE) - java.net.SocketInputStream.socketRead0(java.io.FileDescriptor, byte[], int, int, int) @bci=0 (Compiled frame; information may be imprecise) - java.net.SocketInputStream.socketRead(java.io.FileDescriptor, byte[], int, int, int) @bci=8, line=116 (Compiled frame) - java.net.SocketInputStream.read(byte[], int, int, int) @bci=79, line=170 (Compiled frame) - java.net.SocketInputStream.read(byte[], int, int) @bci=11, line=141 (Compiled frame) - java.io.BufferedInputStream.fill() @bci=214, line=246 (Compiled frame) - java.io.BufferedInputStream.read() @bci=12, line=265 (Compiled frame) - java.io.FilterInputStream.read() @bci=4, line=83 (Compiled frame) - sun.rmi.transport.tcp.TCPTransport.handleMessages(sun.rmi.transport.Connection, boolean) @bci=25, line=550 (Compiled frame) - sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0() @bci=685, line=826 (Interpreted frame) - sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$254() @bci=1, line=683 (Interpreted frame) - sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$274.run() @bci=4 (Interpreted frame) - java.security.AccessController.doPrivileged(java.security.PrivilegedAction, java.security.AccessControlContext) @bci=0 (Compiled frame) - sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run() @bci=58, line=682 (Interpreted frame) - java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) @bci=95, line=1142 (Interpreted frame) - java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=5, line=617 (Interpreted frame) - java.lang.Thread.run() @bci=11, line=745 (Interpreted frame) Thread 36: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Compiled frame; information may be imprecise) - com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run() @bci=227, line=168 (Interpreted frame) - java.lang.Thread.run() @bci=11, line=745 (Interpreted frame) Thread 1: (state = BLOCKED) ... Thread 9: (state = BLOCKED) Thread 8: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Compiled frame; information may be imprecise) - java.lang.ref.ReferenceQueue.remove(long) @bci=59, line=143 (Compiled frame) - java.lang.ref.ReferenceQueue.remove() @bci=2, line=164 (Compiled frame) - java.lang.ref.Finalizer$FinalizerThread.run() @bci=36, line=209 (Interpreted frame) Thread 7: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Compiled frame; information may be imprecise) - java.lang.Object.wait() @bci=2, line=502 (Interpreted frame) - java.lang.ref.Reference$ReferenceHandler.run() @bci=36, line=157 (Interpreted frame)
上面命令使用 -F 选项强制打印堆栈信息。
(3)打印关于锁的附加信息,如下:
D:\share_dir\ShareDoc> jstack -l 2256 2024-07-08 22:42:07 Full thread dump OpenJDK 64-Bit Server VM (17.0.6+10-b829.9 mixed mode): Threads class SMR info: _java_thread_list=0x0000018ab8d79a40, length=63, elements={ 0x000001899fc74940, 0x00000189c81fc660, 0x00000189c81ff760, 0x00000189c8214400, 0x00000189c8c10940, 0x00000189c8c2a840, 0x00000189c8c2d110, 0x00000189c8c31200, 0x00000189c8c323d0, 0x00000189c8c49610, 0x00000189c8d680d0, 0x00000189c97f5bd0, 0x000001899fc756c0, 0x00000189f3a82010, 0x00000189f3ab3660, 0x00000189f3ab4000, 0x00000189f39e8010, 0x00000189f39e89b0, 0x00000189f39e6800, 0x00000189f39e7b40, 0x00000189f39e1630, 0x00000189f39e24a0, 0x00000189f39e5990, 0x00000189f39e2e40, 0x0000018a212d0560, 0x0000018a684c0a20, 0x0000018a6a129310, 0x0000018a72a08220, 0x0000018a72a0a3d0, 0x0000018a72a09560, 0x0000018a72a0a8a0, 0x0000018a6fb6f6c0, 0x00000189c94e9290, 0x0000018a74558620, 0x0000018a745611b0, 0x0000018a745671f0, 0x0000018a74568530, 0x0000018a75d3e320, 0x0000018aa6056740, 0x0000018aa60558d0, 0x0000018aa6058dc0, 0x0000018aa603dca0, 0x0000018aa60459c0, 0x0000018aa603f4b0, 0x0000018aa7a90c40, 0x0000018aa7a9b980, 0x00000189f3ab27f0, 0x00000189f3a829b0, 0x0000018a7455eb30, 0x0000018a7455b170, 0x0000018a75d404d0, 0x0000018a75d33ab0, 0x0000018aa6041660, 0x0000018aa603bfc0, 0x0000018a6fb73a20, 0x0000018a6fb790c0, 0x0000018aa7a82070, 0x00000189f39e3310, 0x00000189f39e1160, 0x0000018a745646a0, 0x0000018a72a0b240, 0x0000018a72a13430, 0x0000018aa7ab6aa0 } "main" #1 prio=5 os_prio=0 cpu=2109.38ms elapsed=9711.61s tid=0x000001899fc74940 nid=0x2fc8 waiting on condition [0x00000025756fe000] java.lang.Thread.State: TIMED_WAITING (parking) at jdk.internal.misc.Unsafe.park(java.base@17.0.6/Native Method) - parking to wait for <0x00000000c2dc66e8> (a kotlinx.coroutines.BlockingCoroutine) at java.util.concurrent.locks.LockSupport.parkNanos(java.base@17.0.6/LockSupport.java:252) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:88) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at com.intellij.idea.Main.main(Main.kt:40) Locked ownable synchronizers: - None ... "VM Thread" os_prio=2 cpu=765.62ms elapsed=9711.57s tid=0x00000189c81d5630 nid=0x5434 runnable "GC Thread#0" os_prio=2 cpu=2531.25ms elapsed=9711.61s tid=0x000001899fcb0270 nid=0x5224 runnable "GC Thread#1" os_prio=2 cpu=2296.88ms elapsed=9710.88s tid=0x00000189c8f40ed0 nid=0x5b08 runnable "GC Thread#2" os_prio=2 cpu=2531.25ms elapsed=9710.88s tid=0x00000189c9155340 nid=0x4bd8 runnable "GC Thread#3" os_prio=2 cpu=2843.75ms elapsed=9710.88s tid=0x00000189c9277ca0 nid=0x410c runnable "G1 Main Marker" os_prio=2 cpu=46.88ms elapsed=9711.61s tid=0x000001899fcb9950 nid=0x3a90 runnable "G1 Conc#0" os_prio=2 cpu=22375.00ms elapsed=9711.61s tid=0x000001899fcba480 nid=0xabc runnable "G1 Refine#0" os_prio=2 cpu=203.12ms elapsed=9711.61s tid=0x000001899fd13400 nid=0x31d8 runnable "G1 Refine#1" os_prio=2 cpu=0.00ms elapsed=9709.17s tid=0x00000189f31cb170 nid=0x5cec runnable "G1 Service" os_prio=2 cpu=218.75ms elapsed=9711.61s tid=0x000001899fd13e20 nid=0x3c4c runnable "VM Periodic Task Thread" os_prio=2 cpu=406.25ms elapsed=9710.66s tid=0x00000189c93df610 nid=0x4c54 waiting on condition JNI global refs: 197, weak refs: 443 JNI global refs memory usage: 2739, weak refs: 13041 JNI global refs: 197, weak refs: 443
上面例子使用 -l 选项可以打印出关于锁的附加信息,这有助于诊断死锁等问题。