Somnath Musib 2024-05-11
1. 概述
在本教程中,我们将讨论多种捕获 Java 应用程序线程转储(thread dump)的方法。
线程转储是 Java 进程中所有线程状态的快照。每个线程的状态通过堆栈跟踪(stack trace)呈现,展示了该线程堆栈的内容。线程转储对于诊断问题非常有用,因为它能显示线程的活动情况。线程转储以纯文本格式输出,因此我们可以将其内容保存到文件中,稍后使用文本编辑器进行查看。
在接下来的章节中,我们将介绍多种工具和方法来生成线程转储。
2. 使用 JDK 工具
JDK 提供了多个实用工具,可用于捕获 Java 应用程序的线程转储。这些工具都位于 JDK 安装目录下的 bin 文件夹中。只要该目录已加入系统路径,我们就可以从命令行直接执行这些工具。
2.1. jstack
jstack 是一个命令行 JDK 工具,可用于捕获线程转储。它接受一个进程 ID(pid),并在控制台中显示线程转储内容。也可以将输出重定向到文件中。
基本命令语法如下:
jstack [-F] [-l] [-m] <pid>
所有选项均为可选,其含义如下:
-F:强制生成线程转储;当jstack <pid>无响应时(例如进程挂起),此选项非常有用。-l:指示工具查找堆中的可拥有的同步器(ownable synchronizers)和锁信息。-m:除了 Java 堆栈帧外,还打印本地(C/C++)堆栈帧。
下面是一个将线程转储输出重定向到文件的示例:
jstack 17264 > /tmp/threaddump.txt
提示:可以使用
jps命令轻松获取 Java 进程的 pid。
2.2. Java Mission Control(JMC)
Java Mission Control(JMC)是一个图形界面工具,用于收集和分析 Java 应用程序的数据。启动 JMC 后,它会列出本地机器上运行的所有 Java 进程,也支持连接远程 Java 进程。
右键点击目标进程并选择 “Start Flight Recording”(开始飞行记录),随后在 Threads(线程) 标签页中即可查看线程转储。
2.3. jvisualvm
jvisualvm 是一个带有图形用户界面的工具,可用于监控、故障排查和分析 Java 应用程序。其界面简洁直观,易于使用。
在众多功能中,它支持捕获线程转储。只需右键点击 Java 进程并选择 “Thread Dump”(线程转储)选项,工具就会生成线程转储并在新标签页中打开。
注意:从 JDK 9 开始,Oracle JDK 和 OpenJDK 不再包含 VisualVM。如果使用 Java 9 或更高版本,可以从 VisualVM 开源项目网站 下载安装。
2.4. jcmd
jcmd 是一个向 JVM 发送命令请求的工具。虽然功能强大,但它不支持远程操作,必须在运行 Java 进程的同一台机器上使用。
其中一个命令是 Thread.print,可用于获取线程转储,只需指定进程 ID 即可:
jcmd 17264 Thread.print
2.5. jconsole
jconsole 允许我们查看每个线程的堆栈跟踪。启动 jconsole 并连接到正在运行的 Java 进程后,切换到 Threads(线程) 标签页,即可看到每个线程的堆栈信息。
2.6. 小结
JDK 提供了多种捕获线程转储的方式,各有优劣:
| 工具 | 优点 | 缺点 |
|---|---|---|
jstack |
快速简单 | 自 Java 8 起已有更优替代方案 |
jmc |
高级诊断与性能分析工具,开销低 | 功能较复杂 |
jvisualvm |
轻量级、开源、图形界面友好 | JDK 9+ 不再内置 |
jcmd |
功能强大,推荐用于 Java 8+;集成了 jstack、jmap、jinfo 等功能 |
仅限本地使用 |
jconsole |
可查看线程堆栈信息 | 功能相对基础 |
3. 通过命令行方式
在企业应用服务器中,出于安全考虑通常只安装 JRE,而上述 JDK 工具不可用。不过,我们仍可通过以下命令行方式轻松捕获线程转储。
3.1. kill -3 命令(Linux/Unix)
在类 Unix 系统中,最简单的方法是使用 kill 命令向 Java 进程发送 SIGQUIT(信号编号 3):
kill -3 17264
Java 进程接收到该信号后,会将线程转储打印到标准输出(通常是启动该进程的终端或日志文件)。
如果 Java 进程启动时添加了以下 JVM 参数,则线程转储还会被写入指定的日志文件:
-XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=~/jvm.log
此时,发送 kill -3 信号后,线程转储将同时出现在标准输出和 ~/jvm.log 文件中。
3.2. Ctrl + Break(Windows)
在 Windows 系统中,可以通过组合键 Ctrl + Break 捕获线程转储。具体操作:切换到启动 Java 应用程序的控制台窗口,同时按下 Ctrl 和 Break 键。
注意:某些键盘没有
Break键,此时可使用Ctrl + Shift + Pause组合键代替。
这两种方式都会将线程转储输出到控制台。
4. 通过编程方式使用 ThreadMXBean
最后一种方法是使用 JMX(Java Management Extensions)API,通过 ThreadMXBean 在代码中生成线程转储。示例如下:
private static String threadDump(boolean lockedMonitors, boolean lockedSynchronizers) {
StringBuffer threadDump = new StringBuffer(System.lineSeparator());
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads(lockedMonitors, lockedSynchronizers)) {
threadDump.append(threadInfo.toString());
}
return threadDump.toString();
}
代码说明:
- 初始化一个
StringBuffer用于存储所有线程的堆栈信息。 - 通过
ManagementFactory.getThreadMXBean()获取ThreadMXBean实例,它是 JVM 线程系统的管理接口。 - 调用
dumpAllThreads(lockedMonitors, lockedSynchronizers)方法:- 若
lockedMonitors为true,则包含所有被持有的监视器锁(monitors)。 - 若
lockedSynchronizers为true,则包含可拥有的同步器(如ReentrantLock)。
- 若
5. 结论
本文介绍了多种捕获 Java 线程转储的方法:
- 首先,我们探讨了多种 JDK 内置工具(如
jstack、jcmd、jvisualvm等); - 接着,介绍了在 仅有 JRE 的生产环境 中使用的命令行方法(如
kill -3和Ctrl+Break); - 最后,展示了如何通过 JMX API 编程方式 生成线程转储。
根据实际环境和需求,可以选择最适合的方式来获取线程转储,从而高效地诊断和解决 Java 应用中的并发问题。