捕获Java线程转储

2023/07/04

1. 概述

在本教程中,我们将讨论捕获Java应用程序线程转储的各种方法。

线程转储是Java进程所有线程状态的快照,每个线程的状态都以堆栈跟踪的形式呈现,显示线程堆栈的内容。线程转储可用于诊断问题,因为它显示线程的活动。线程转储以纯文本形式编写,因此我们可以将它们的内容保存到一个文件中,稍后在文本编辑器中查看它们

在接下来的部分中,我们将通过多种工具和方法来生成线程转储。

2. 使用JDK实用程序

JDK提供了几个实用程序,可以捕获Java应用程序的线程转储。所有实用程序都位于JDK主目录中的bin文件夹下,因此,只要这个目录在我们的系统路径中,我们就可以从命令行执行这些实用程序。

2.1 jstack

jstack是一个命令行JDK实用程序,我们可以使用它来捕获线程转储,它获取进程的pid并在控制台中显示线程转储。或者,我们可以将其输出重定向到一个文件。

让我们看一下使用jstack捕获线程转储的基本命令语法

jstack [-F] [-l] [-m] <pid>

所有标志都是可选的,让我们看看他们的意思:

  • -F选项强制线程转储;当jstack pid没有响应时很方便(进程挂起)
  • -l选项指示实用程序在堆和锁中查找可拥有的同步器
  • -m选项除了Java堆栈帧之外,还打印本机堆栈帧(C和C++)

让我们通过捕获线程转储并将结果重定向到文件来使用这些知识:

jstack 17264 > /tmp/threaddump.txt

请记住,我们可以使用jps命令轻松获取Java进程的pid。

2.2 Java Mission Control

Java Mission Control(JMC)是一个GUI工具,用于收集和分析来自Java应用程序的数据。启动JMC后,它会显示在本地机器上运行的Java进程列表。我们还可以通过JMC连接到远程Java进程。

我们可以在进程上右击,点击“Start Flight Recording”选项。在此之后,Threads选项卡显示线程转储:

江铃汽车

2.3 jvisualvm

jvisualvm是一个带有图形用户界面的工具,可以让我们监控、排除故障和分析Java应用程序。GUI很简单,但非常直观且易于使用。

它的众多选项之一允许我们捕获线程转储,如果我们右键单击一个Java进程并选择“Thread Dump”选项,该工具将创建一个线程转储并在新选项卡中打开它:

JVisual虚拟机

从JDK 9开始,Visual VM不包含在OracleJDK和OpenJDK发行版中。因此,如果我们使用的是Java 9或更新版本,我们可以从Visual VM开源项目站点获取JVisualVM。

2.4 jcmd

jcmd是一个通过向JVM发送命令请求来工作的工具。尽管功能强大,但它不包含任何远程功能;我们必须在运行Java进程的同一台机器上使用它。

它的众多命令之一是Thread.print,我们可以通过指定进程的pid来使用它来获取线程转储:

jcmd 17264 Thread.print

2.5 jconsole

jconsole允许我们检查每个线程的堆栈跟踪,如果我们打开jconsole并连接到一个正在运行的Java进程,我们可以导航到Threads选项卡并找到每个线程的堆栈跟踪

J控制台

2.6 概括

事实证明,有许多方法可以使用JDK实用程序捕获线程转储,让我们花点时间来总结一下,并概述它们的优缺点:

  • jstack:提供最快和最简单的方法来捕获线程转储;但是,从Java 8开始可以使用更好的替代方案
  • jmc:增强的JDK分析和诊断工具,它最大限度地减少了通常是分析工具问题的性能开销
  • jvisualvm:具有出色GUI控制台的轻量级开源分析工具
  • jcmd:非常强大,推荐用于Java 8及更高版本。具有多种用途的单一工具:捕获线程转储(jstack)、堆转储(jmap)、系统属性和命令行参数(jinfo)
  • jconsole:允许我们检查线程堆栈跟踪信息

3. 命令行

在企业级应用程序服务器中,出于安全原因,仅安装JRE。因此,我们不能使用上述实用程序,因为它们是JDK的一部分。但是,有各种命令行替代方案可以让我们轻松捕获线程转储。

3.1 kill -3命令(Linux/Unix)

在类Unix系统中捕获线程转储的最简单方法是通过kill命令,我们可以使用该命令向使用kill()系统调用的进程发送信号。在这个用例中,我们将向它发送-3信号。

使用前面示例中的相同pid,让我们看一下如何使用kill捕获线程转储:

kill -3 17264

这样,接收信号的Java进程将在标准输出上打印线程转储。

如果我们使用以下调整标志组合运行Java进程,那么它还会将线程转储重定向到给定文件:

-XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=~/jvm.log

现在,如果我们发送-3信号,除了标准输出之外,转储将在~/jvm.log文件中可用。

3.2 Ctrl + Break(Windows)

在Windows操作系统中,我们可以使用CTRL和Break组合键捕获线程转储。要获取线程转储,请导航至用于启动Java应用程序的控制台,然后同时按下CTRL和Break键。

值得注意的是,在某些键盘上,Break键不可用。因此,在这种情况下,可以同时使用CTRL、SHIFT和Pause键来捕获线程转储。

这两个命令都将线程转储打印到控制台。

4. 以编程方式使用ThreadMxBean

我们将在本文中讨论的最后一种方法是使用JMX我们将使用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();
}

在上面的程序中,我们执行了几个步骤:

  1. 首先初始化一个空的StringBuffer,用来保存各个线程的栈信息
  2. 然后我们使用ManagementFactory类来获取ThreadMxBean的实例,ManagementFactory是为Java平台获取托管bean的工厂类。另外,ThreadMxBean是JVM线程系统的管理接口
  3. 将lockedMonitors和lockedSynchronizers值设置为true表示在线程转储中捕获可拥有的同步器和所有锁定的监视器

5. 总结

在本文中,我们学习了多种捕获线程转储的方法。

首先,我们讨论了各种JDK实用程序,然后是命令行选项。最后,我们总结了使用JMX的编程方法。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章