📜  捕获Java堆转储的不同方法

📅  最后修改于: 2022-05-13 01:55:43.509000             🧑  作者: Mango

捕获Java堆转储的不同方法

Head dump 是某个时刻在 JVM 内存中的所有对象的快照。它们对于解决内存泄漏问题和优化Java应用程序中的内存使用非常有用。头部转储通常存储在二进制格式的“hprof”文件中。我们可以使用诸如“jhat”或“JVisualVM”之类的工具打开和分析这些文件。此外,对于 Eclipse 用户来说,使用 MAT 是很常见的。现在我们将通过多种工具和方法来生成一个头驼峰,我们将展示它们之间的主要区别。

JDK 附带了多种工具,可以以不同的方式捕获磁头转储。所有这些工具都位于 JDK 主目录中的 bin 文件夹下。因此,只要此目录包含在系统路径中,我们就可以从命令行启动它们。

在接下来的步骤中,我们将看到如何使用这些工具来捕获磁头转储。

方法:

  1. 地图
  2. jcmd
  3. 虚拟机
  4. 自动捕获头转储
  5. JMX

让我们详细讨论上面列出的每种方法,以更好地理解它们。



方法一: jmap

jmap 是一种用于打印正在运行的 JVM 中的内存统计信息的工具。我们可以将它用于本地或远程进程。要使用 jmap 捕获 head dump,我们需要使用 dump 选项:

jmap -dump:[live],format=b,file= 

除了该选项,我们还应该指定几个参数:

  • live: 如果设置它只打印具有活动引用的对象并丢弃准备好被垃圾收集的对象。这个参数是可选的
  • format=b:指定转储文件为二进制格式。如果没有设置结果是一样的
  • 文件:转储将写入的文件
  • pid: Java进程的id

例子

jmap -dump:live,format=b,file=/tmp/dump.hprof 12587

方法二: jcmd

'jcmd' 是一个 非常完整的工具,通过向 JVM 发送命令请求来工作。我们必须在运行Java进程的同一台机器上使用它。它的众多命令之一是 GC.heap-dump。  我们可以使用它来获取堆转储,只需指定进程的 pid 和输出文件路径:

jcmd  GC.head_dump 

我们可以使用我们之前使用的相同参数来执行它:



jcmd 12587  GC.head_dump  /tmp/dump.hprof

与 jmap 一样,生成的转储采用二进制格式。

方法三: JVisualVM

JVisualVM 是一个带有图形用户界面的工具,可让我们监视故障排除和分析Java应用程序。 GUI 很简单,但非常直观且易于使用。它的众多选项之一允许我们捕获头部转储。如果我们右键单击Java进程并选择 Head Dump”选项,该工具将创建一个堆转储并在新选项卡中打开它:

正如我们在上面注意到的,我们可以找到在“基本信息”部分中创建的文件的路径。从 JDK9 开始,VISUAL VM 不包含在 Oracle JDK 和 Open JDK 发行版中。因此,如果我们使用Java 9 或更新版本,我们可以从 Visual VM 开源项目站点获取 JVisualVM。

方法四:自动捕获磁头转储

我们上面展示的所有工具都旨在在特定时间手动捕获磁头转储。在某些情况下,我们希望在发生Java.lang.OutOfMemoryError 时获得堆转储,以便帮助我们调查错误:

对于这些情况, Java提供 HeadDumpOnOutOfMemoryError 命令行选项,该选项在抛出Java.lang.OutOfMemoryError 时生成堆转储:

java -XX:+HeadDumpOnOutOfMemoryError

默认情况下,它将转储存储在我们运行应用程序的目录中的java_pid.hprof文件中。如果我们想指定另一个文件或目录,我们可以在 HeadDumpPath 选项中设置它:

java -XX:+HeadDumpOnOutOfMemoryError -XX:HeapDumpPath=

当我们的应用程序使用此选项耗尽内存时,我们将能够在日志中看到包含堆转储的创建文件:



java.lang.OutOfMemoryError: Requested array size exceeds VM limit
During heap to java_pid12587.hprof...
  Exception in thread "main" Head dump file created [4744371 bytes in 0.029 secs]
  java.lang.OutOfMemoryError: Requested array size exceeds VM limit 
  at com.baeldung.heapdump.App.main(App.java:7)

在上面的这个例子中,它被写入 java_pid2587.hprof 文件。正如我们所见,此选项非常有用,使用此选项运行应用程序时没有开销。因此,强烈建议始终使用此选项,尤其是在生产中。最后,还可以在运行时使用HotSpotDiagnostic MBean指定此选项。为此,我们可以使用JConsole并将HeapDumpOnOutOfMemoryError VM 选项设置为 true:

方法五: JMX

我们将在本文中介绍的最后一种方法是使用 JMX。我们将使用我们在上一节中简要介绍的 HotSpotDiagnostic MBean。这个 MBean 提供了一个接受 2 个参数的 dumpHeap 方法:

  • outputFile:转储文件的路径。该文件应具有 hprof 扩展名
  • live:如果设置为 true,它只转储内存中的活动对象,正如我们之前看到的 jmap

现在这里有两种方法可以调用此方法以捕获头部转储:

使用 HotSpotDiagnostic MBean 的最简单方法是使用 JMX 客户端,例如 JConsole。如果我们打开 JConsole 并连接到正在运行的Java进程,我们可以导航到 MBeans 选项卡并在com.sun.management下找到 HotSpotDiagnostic 在这些操作中,我们可以找到之前介绍过的 dumpHead 方法:

如图所示,我们只需要在 p0 和 p1 文本字段中引入参数 outputFile 和 live 即可执行 dumpHeap 操作。

实现:捕获堆转储的方法

使用 HotSpotDiagnostic MBean 的另一种方法是从Java代码中以编程方式调用它。为此,我们需要获取一个 MBeanServer 实例,以便获取在应用程序中注册的 MBean。之后,我们只需要获取一个 HotSpotDiagnosticMXBean 的实例并调用它的 dumpHeap 方法。

public static void dumpHeap(String filePath, boolean live) throws IOException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy
  (server, "com.sun.management:type=HotSpotDiagnos
  tic",
HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
}

请注意,无法覆盖hprof文件。因此,我们在创建打印堆转储的应用程序时应该考虑到这一点。如果我们不这样做,我们将得到一个异常:

Exception in thread "main" java.io.IOException: File exists 
at sun.management.HotSpotDianostic.dumpHeap0(Native Method) at 
sun.management.HotSpotDiagnostic.dumpHeap(HotSpotDiagnostic.java:60)