Diagnosing a Running JVM – 诊断一个正在运行的JVM

最后修改: 2021年 8月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

The Java Virtual Machine (JVM) is a virtual machine that enables a computer to run Java programs. In this article, we’ll see how we can diagnose a running JVM with ease.

Java虚拟机(JVM)是一个虚拟机,使计算机能够运行Java程序。在这篇文章中,我们将看到如何轻松地诊断一个正在运行的JVM。

We have many tools available in the JDK itself which can be used for various development, monitoring, and troubleshooting activities. Let’s have a look at jcmd, which is quite easy to use and can provide a variety of information about a running JVM. In addition, jcmd is a recommended tool from JDK 7 onwards for enhanced JVM diagnostics with no or minimum performance overhead.

我们有许多工具在JDK本身中可用,可用于各种开发、监控和故障排除活动。让我们来看看jcmd,它相当容易使用,可以提供有关运行中的JVM的各种信息。此外,从JDK 7开始,jcmd是一个值得推荐的工具,可以在没有性能开销或性能开销最小的情况下增强JVM的诊断能力。

2. What Is jcmd?

2.什么是jcmd

This is a utility that sends diagnostic command requests to a running JVM. However, it must be used on the same machine on which JVM is running. Additional details are available in its documentation.

这是一个向正在运行的JVM发送诊断命令请求的工具。然而,它必须在运行JVM的同一台机器上使用。更多细节可在其文档中找到。

Let’s see how we can use this utility with a sample Java application running on a server.

让我们看看我们如何用一个运行在服务器上的样本Java应用程序来使用这个工具。

3. How to Use jcmd?

3.如何使用jcmd

Let’s create a quick demo web application using Spring Initializr with JDK11. Now, let’s start the server and diagnose it using jcmd.

让我们使用Spring InitializrJDK11创建一个快速演示Web应用程序。现在,让我们启动服务器并使用jcmd对其进行诊断。

3.1. Getting the PID

3.1.获得PID

We know that each process has an associated process id known as PID. Hence to get the associated PID for our application, we can use jcmd which will list all applicable Java processes as below:

我们知道,每个进程都有一个相关的进程ID,称为PID。因此,为了获得我们应用程序的相关PID,我们可以使用jcmd,它将列出所有适用的Java进程,如下所示。

root@c6b47b129071:/# jcmd
65 jdk.jcmd/sun.tools.jcmd.JCmd
18 /home/pgm/demo-0.0.1-SNAPSHOT.jar
root@c6b47b129071:/# 

Here, we can see the PID of our running application is 18.

在这里,我们可以看到我们正在运行的应用程序的PID是18.

3.2. Get List of Possible jcmd Usage

3.2.获取可能的jcmd用法列表

Let’s find out possible options available with the jcmd PID help command to start with:

让我们从jcmd PID help命令开始,找出可用的选项。

root@c6b47b129071:/# jcmd 18 help
18:
The following commands are available:
Compiler.CodeHeap_Analytics
Compiler.codecache
Compiler.codelist
Compiler.directives_add
Compiler.directives_clear
Compiler.directives_print
Compiler.directives_remove
Compiler.queue
GC.class_histogram
GC.class_stats
GC.finalizer_info
GC.heap_dump
GC.heap_info
GC.run
GC.run_finalization
JFR.check
JFR.configure
JFR.dump
JFR.start
JFR.stop
JVMTI.agent_load
JVMTI.data_dump
ManagementAgent.start
ManagementAgent.start_local
ManagementAgent.status
ManagementAgent.stop
Thread.print
VM.class_hierarchy
VM.classloader_stats
VM.classloaders
VM.command_line
VM.dynlibs
VM.flags
VM.info
VM.log
VM.metaspace
VM.native_memory
VM.print_touched_methods
VM.set_flag
VM.stringtable
VM.symboltable
VM.system_properties
VM.systemdictionary
VM.uptime
VM.version
help

The available diagnostic commands may be different in different versions of HotSpot VM.

不同版本的HotSpot VM的可用诊断命令可能不同。

4. jcmd Commands

4.jcmd命令

Let’s explore some of the most useful jcmd command options to diagnose our running JVM.

让我们来探索一些最有用的jcmd命令选项,以诊断我们正在运行的JVM。

4.1. VM.version

4.1. VM.版本

This is to get JVM basic details as shown below:

这是为了获得JVM的基本细节,如下图所示。

root@c6b47b129071:/# jcmd 18 VM.version
18:
OpenJDK 64-Bit Server VM version 11.0.11+9-Ubuntu-0ubuntu2.20.04
JDK 11.0.11
root@c6b47b129071:/# 

Here we can see that we are using OpenJDK 11 for our sample application.

在这里我们可以看到,我们的示例应用程序使用的是OpenJDK 11。

4.2. VM.system_properties

4.2.VM.system_properties

This will print all the system properties set for our VM. There can be several hundred lines of information displayed:

这将打印为我们的虚拟机设置的所有系统属性。可以有几百行的信息显示。

root@c6b47b129071:/# jcmd 18 VM.system_properties
18:
#Thu Jul 22 10:56:13 IST 2021
awt.toolkit=sun.awt.X11.XToolkit
java.specification.version=11
sun.cpu.isalist=
sun.jnu.encoding=ANSI_X3.4-1968
java.class.path=/home/pgm/demo-0.0.1-SNAPSHOT.jar
java.vm.vendor=Ubuntu
sun.arch.data.model=64
catalina.useNaming=false
java.vendor.url=https\://ubuntu.com/
user.timezone=Asia/Kolkata
java.vm.specification.version=11
...

4.3. VM.flags

4.3. VM.flags

For our sample application, this will print all VM arguments used, either given by us or used default by JVM. Here, we can notice various default VM arguments as below:

对于我们的示例应用程序,这将打印所有使用的虚拟机参数,无论是我们给出的还是JVM默认使用的。在这里,我们可以注意到各种默认的虚拟机参数,如下所示。

root@c6b47b129071:/# jcmd 18 VM.flags            
18:
-XX:CICompilerCount=3 -XX:CompressedClassSpaceSize=260046848 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=4 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=536870912 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=536870912 -XX:MaxMetaspaceSize=268435456 -XX:MaxNewSize=321912832 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=5830732 -XX:NonProfiledCodeHeapSize=122913754 -XX:ProfiledCodeHeapSize=122913754 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:ThreadStackSize=256 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC 
root@c6b47b129071:/#

Similarly, other commands, like VM.command_line, VM.uptime, VM.dynlibs, also provide other basic and useful details about various other properties used.

同样,其他命令,如VM.command_line,VM.uptime,VM.dynlibs,也提供其他基本和有用的细节,关于各种其他使用的属性。

All of the above commands are to majorly get different JVM-related details. Now let’s look into some more commands that can help in some troubleshooting related to JVM.

以上所有的命令主要是为了获得不同的JVM相关细节。现在让我们再来看看一些可以帮助解决与JVM有关的故障的命令。

4.4. Thread.print

4.4.Thread.print

This command is to get the instant thread dump. Hence, it will print the stack trace of all running threads. Following is the way to use it, which can give long output depending on the number of threads in use:

这个命令是为了获得即时的线程转储。因此,它将打印所有运行线程的堆栈跟踪。以下是使用该命令的方法,根据使用的线程数量,可以得到较长的输出。

root@c6b47b129071:/# jcmd 18 Thread.print
18:
2021-07-22 10:58:08
Full thread dump OpenJDK 64-Bit Server VM (11.0.11+9-Ubuntu-0ubuntu2.20.04 mixed mode, sharing):

Threads class SMR info:
_java_thread_list=0x00007f21cc0028d0, length=25, elements={
0x00007f2210244800, 0x00007f2210246800, 0x00007f221024b800, 0x00007f221024d800,
0x00007f221024f800, 0x00007f2210251800, 0x00007f2210253800, 0x00007f22102ae800,
0x00007f22114ef000, 0x00007f21a44ce000, 0x00007f22114e3800, 0x00007f221159d000,
0x00007f22113ce800, 0x00007f2210e78800, 0x00007f2210e7a000, 0x00007f2210f20800,
0x00007f2210f22800, 0x00007f2210f24800, 0x00007f2211065000, 0x00007f2211067000,
0x00007f2211069000, 0x00007f22110d7800, 0x00007f221122f800, 0x00007f2210016000,
0x00007f21cc001000
}

"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=2.32ms elapsed=874.34s tid=0x00007f2210244800 nid=0x1a waiting on condition  [0x00007f221452a000]
   java.lang.Thread.State: RUNNABLE
	at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.11/Native Method)
	at java.lang.ref.Reference.processPendingReferences(java.base@11.0.11/Reference.java:241)
	at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.11/Reference.java:213)

"Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.32ms elapsed=874.34s tid=0x00007f2210246800 nid=0x1b in Object.wait()  [0x00007f22144e9000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(java.base@11.0.11/Native Method)
	- waiting on <0x00000000f7330898> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(java.base@11.0.11/ReferenceQueue.java:155)
	- waiting to re-lock in wait() <0x00000000f7330898> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(java.base@11.0.11/ReferenceQueue.java:176)
	at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.11/Finalizer.java:170)

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.40ms elapsed=874.33s tid=0x00007f221024b800 nid=0x1c runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

Detailed discussion on capturing a thread dump using other options can be found here.

关于使用其他选项捕获线程转储的详细讨论可以在这里找到。

4.5. GC.class_histogram

4.5.GC.class_histogram

Let’s use another jcmd command that will provide important information about heap usage. Additionally, this will list all classes (either external or application-specific) with many instances. Again, the list can be of hundreds of lines depending on the number of classes in use:

让我们使用另一个jcmd命令,它将提供关于堆使用的重要信息。此外,这将列出所有具有许多实例的类(无论是外部的还是特定的应用程序)。同样,这个列表可以有几百行,这取决于使用中的类的数量。

root@c6b47b129071:/# jcmd 18 GC.class_histogram
18:
 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:         41457        2466648  [B (java.base@11.0.11)
   2:         38656         927744  java.lang.String (java.base@11.0.11)
   3:          6489         769520  java.lang.Class (java.base@11.0.11)
   4:         21497         687904  java.util.concurrent.ConcurrentHashMap$Node (java.base@11.0.11)
   5:          6570         578160  java.lang.reflect.Method (java.base@11.0.11)
   6:          6384         360688  [Ljava.lang.Object; (java.base@11.0.11)
   7:          9668         309376  java.util.HashMap$Node (java.base@11.0.11)
   8:          7101         284040  java.util.LinkedHashMap$Entry (java.base@11.0.11)
   9:          3033         283008  [Ljava.util.HashMap$Node; (java.base@11.0.11)
  10:          2919         257000  [I (java.base@11.0.11)
  11:           212         236096  [Ljava.util.concurrent.ConcurrentHashMap$Node; (java.base@11.0.11)

However, if this doesn’t give a clear picture, we can get a heap dump. Let’s look at it next.

然而,如果这不能给人一个清晰的印象,我们可以得到一个堆积的垃圾。让我们接下来看看。

4.6. GC.heap_dump

4.6.GC.heap_dump

This command will give an instant JVM heap dump. Therefore we can extract heap dump into a file to analyze later as below:

这个命令将立即提供JVM堆转储。因此,我们可以将堆转储提取到一个文件中,以便以后进行分析,如下所示。

root@c6b47b129071:/# jcmd 18 GC.heap_dump ./demo_heap_dump
18:
Heap dump file created
root@c6b47b129071:/# 

Here, demo_heap_dump is the heap dump file name. In addition, this will be created at the same location where our application jar is located.

这里,demo_heap_dump是堆转储文件名。此外,这将在我们的应用程序jar所在的同一位置创建。

4.7. JFR Command Options

4.7.JFR 命令选项

In our earlier article, we discussed Java application monitoring using JFR and JMC. Now, let’s look into the jcmd commands that we can use to analyze performance issues with our application.

在我们之前的文章中,我们讨论了使用JFRJMC对 Java 应用程序进行监控。现在,让我们来了解一下jcmd命令,我们可以用它来分析应用程序的性能问题。

JFR (or Java Flight Recorder) is a profiling and event collection framework built into the JDK. JFR allows us to gather detailed low-level information about how JVM and Java applications are behaving. In addition, we can use JMC to visualize the data collected by JFR. Hence, JFR and JMC together create a complete toolchain to continuously collect low-level and detailed runtime information.

JFR(或Java Flight Recorder)是一个内置于JDK的剖析和事件收集框架。JFR允许我们收集有关JVM和Java应用程序行为方式的详细低级信息。此外,我们可以使用JMC来可视化由JFR收集的数据。因此,JFRJMC共同创建了一个完整的工具链,以持续收集低级别的详细运行时信息。

Although how to use JMC is not in the scope of this article, we will see how we can create a JFR file using jcmd. JFR is a commercial feature. Hence by default, it’s disabled.  However, that can be enabled using ‘jcmd PID VM.unlock_commercial_features‘.

尽管如何使用JMC不在本文的范围内,我们将看到如何使用jcmd创建JFR文件。JFR是一个商业功能。因此在默认情况下,它是禁用的。 然而,可以使用’jcmd PID VM.unlock_commercial_features‘来启用。

However, we have used OpenJDK for our article. Hence JFR is enabled for us. Now let’s generate a JFR file using the jcmd command as below:

然而,我们在文章中使用了OpenJDK。因此,JFR对我们来说是启用的。现在让我们使用jcmd命令生成一个JFR文件,如下所示。

root@c6b47b129071:/# jcmd 18 JFR.start name=demo_recording settings=profile delay=10s duration=20s filename=./demorecording.jfr
18:
Recording 1 scheduled to start in 10 s. The result will be written to:

/demorecording.jfr
root@c6b47b129071:/# jcmd 18 JFR.check
18:
Recording 1: name=demo_recording duration=20s (delayed)
root@c6b47b129071:/# jcmd 18 JFR.check
18:
Recording 1: name=demo_recording duration=20s (running)
root@c6b47b129071:/# jcmd 18 JFR.check
18:
Recording 1: name=demo_recording duration=20s (stopped)

We have created a sample JFR recording file name demorecording.jfr at the same location where our jar application is located. Additionally, this recording is of 20seconds and configured as per requirements.

我们已经创建了一个样本JFR录音文件,名称为demorecording.jfr,位于我们的jar应用程序所在的同一位置。此外,这个录音是20秒的,并按照要求进行配置。

In addition, we can check the status of the JFR recording using the JFR.check command. And, we can instantly stop and discard the recording using the JFR.stop command. On the other hand, the JFR.dump command can be used to instantly stop and dump the recording.

此外,我们可以使用JFR.check命令检查JFR录音的状态。而且,我们可以使用JFR.stop命令立即停止并丢弃录音。另一方面,JFR.dump命令可以用来即时停止和转储录音。

4.8. VM.native_memory

4.8.VM.native_memory

This is one of the best commands that can provide a lot of useful details about heap and non-heap memory on a JVM. Therefore, this can be used to tune memory usage and detect any memory leak. As we know, JVM memory can be broadly classified as heap and non-heap memory. And to get the details of complete JVM memory usage, we can use this utility. In addition, this can be useful in defining memory size for a container-based application.

这是一个最好的命令,可以提供很多关于JVM上堆和非堆内存的有用细节。因此,这可以用来调整内存的使用和检测任何内存泄漏。正如我们所知,JVM的内存可以大致分为堆和非堆内存。为了获得完整的JVM内存使用细节,我们可以使用这个工具。此外,这对于定义基于容器的应用程序的内存大小也很有用。

To use this feature we need to restart our application with additional VM argument i.e. –XX:NativeMemoryTracking=summary   or  -XX:NativeMemoryTracking=detail. Note that enabling NMT causes a 5% -10% performance overhead.

为了使用这个功能,我们需要用额外的VM参数重启我们的应用程序,即-XX:NativeMemoryTracking=summary或-XX:NativeMemoryTracking=detail。请注意,启用NMT会导致5%-10%的性能开销。

This will give us a new PID to diagnose:

这将给我们提供一个新的PID来进行诊断。

root@c6b47b129071:/# jcmd 19 VM.native_memory
19:

Native Memory Tracking:

Total: reserved=1159598KB, committed=657786KB
-                 Java Heap (reserved=524288KB, committed=524288KB)
                            (mmap: reserved=524288KB, committed=524288KB) 
 
-                     Class (reserved=279652KB, committed=29460KB)
                            (classes #6425)
                            (  instance classes #5960, array classes #465)
                            (malloc=1124KB #15883) 
                            (mmap: reserved=278528KB, committed=28336KB) 
                            (  Metadata:   )
                            (    reserved=24576KB, committed=24496KB)
                            (    used=23824KB)
                            (    free=672KB)
                            (    waste=0KB =0.00%)
                            (  Class space:)
                            (    reserved=253952KB, committed=3840KB)
                            (    used=3370KB)
                            (    free=470KB)
                            (    waste=0KB =0.00%)
 
-                    Thread (reserved=18439KB, committed=2699KB)
                            (thread #35)
                            (stack: reserved=18276KB, committed=2536KB)
                            (malloc=123KB #212) 
                            (arena=39KB #68)
 
-                      Code (reserved=248370KB, committed=12490KB)
                            (malloc=682KB #3839) 
                            (mmap: reserved=247688KB, committed=11808KB) 
 
-                        GC (reserved=62483KB, committed=62483KB)
                            (malloc=10187KB #7071) 
                            (mmap: reserved=52296KB, committed=52296KB) 
 
-                  Compiler (reserved=146KB, committed=146KB)
                            (malloc=13KB #307) 
                            (arena=133KB #5)
 
-                  Internal (reserved=460KB, committed=460KB)
                            (malloc=428KB #1421) 
                            (mmap: reserved=32KB, committed=32KB) 
 
-                     Other (reserved=16KB, committed=16KB)
                            (malloc=16KB #3) 
 
-                    Symbol (reserved=6593KB, committed=6593KB)
                            (malloc=6042KB #72520) 
                            (arena=552KB #1)
 
-    Native Memory Tracking (reserved=1646KB, committed=1646KB)
                            (malloc=9KB #113) 
                            (tracking overhead=1637KB)
 
-        Shared class space (reserved=17036KB, committed=17036KB)
                            (mmap: reserved=17036KB, committed=17036KB) 
 
-               Arena Chunk (reserved=185KB, committed=185KB)
                            (malloc=185KB) 
 
-                   Logging (reserved=4KB, committed=4KB)
                            (malloc=4KB #191) 
 
-                 Arguments (reserved=18KB, committed=18KB)
                            (malloc=18KB #489) 
 
-                    Module (reserved=124KB, committed=124KB)
                            (malloc=124KB #1521) 
 
-              Synchronizer (reserved=129KB, committed=129KB)
                            (malloc=129KB #1089) 
 
-                 Safepoint (reserved=8KB, committed=8KB)
                            (mmap: reserved=8KB, committed=8KB) 
 

Here, we can notice details about different memory types apart from Java Heap Memory. The Class defines the JVM memory used to store class metadata. Similarly, the Thread defines the memory that our application threads are using. And the Code gives the memory used to store JIT-generated code, the Compiler itself has some space usage, and GC occupies some space too.

在这里,我们可以注意到除了Java Heap Memory之外的不同内存类型的细节。定义了用于存储类元数据的JVM内存。同样地,Thread定义了我们的应用程序线程所使用的内存。而Code给出了用于存储JIT-生成的代码的内存,Compiler本身也有一些空间占用,GC也占据了一些空间。

In addition, the reserved can give an estimation of the memory required for our application. And the committed shows the minimum allocated memory.

此外,reserved可以给我们的应用程序所需的内存一个估计。而committed显示了分配的最小内存。

5. Diagnose Memory Leak

5.诊断内存泄漏

Let’s see how we can identify if there is any memory leak in our JVM. Hence to start with, we need to first have a baseline. And then need to monitor for some time to understand if there is any consistent increase in memory in any of the memory types mentioned above.

让我们看看如何识别JVM中是否存在内存泄漏。因此,首先,我们需要有一个基线。然后需要监测一段时间,以了解上述任何一种内存类型的内存是否有持续增加。

Let’s first baseline the JVM memory usage as below:

让我们首先基线JVM的内存使用,如下所示。

root@c6b47b129071:/# jcmd 19 VM.native_memory baseline
19:
Baseline succeeded

Now, use the application for normal or heavy usage for some time. In the end, just use diff to identify the change since baseline as below:

现在,使用该应用程序进行正常或大量使用一段时间。最后,只需使用diff来识别自baseline以来的变化。

root@c6b47b129071:/# jcmd 19 VM.native_memory summary.diff
19:

Native Memory Tracking:

Total: reserved=1162150KB +2540KB, committed=660930KB +3068KB

-                 Java Heap (reserved=524288KB, committed=524288KB)
                            (mmap: reserved=524288KB, committed=524288KB)
 
-                     Class (reserved=281737KB +2085KB, committed=31801KB +2341KB)
                            (classes #6821 +395)
                            (  instance classes #6315 +355, array classes #506 +40)
                            (malloc=1161KB +37KB #16648 +750)
                            (mmap: reserved=280576KB +2048KB, committed=30640KB +2304KB)
                            (  Metadata:   )
                            (    reserved=26624KB +2048KB, committed=26544KB +2048KB)
                            (    used=25790KB +1947KB)
                            (    free=754KB +101KB)
                            (    waste=0KB =0.00%)
                            (  Class space:)
                            (    reserved=253952KB, committed=4096KB +256KB)
                            (    used=3615KB +245KB)
                            (    free=481KB +11KB)
                            (    waste=0KB =0.00%)
 
-                    Thread (reserved=18439KB, committed=2779KB +80KB)
                            (thread #35)
                            (stack: reserved=18276KB, committed=2616KB +80KB)
                            (malloc=123KB #212)
                            (arena=39KB #68)
 
-                      Code (reserved=248396KB +21KB, committed=12772KB +213KB)
                            (malloc=708KB +21KB #3979 +110)
                            (mmap: reserved=247688KB, committed=12064KB +192KB)
 
-                        GC (reserved=62501KB +16KB, committed=62501KB +16KB)
                            (malloc=10205KB +16KB #7256 +146)
                            (mmap: reserved=52296KB, committed=52296KB)
 
-                  Compiler (reserved=161KB +15KB, committed=161KB +15KB)
                            (malloc=29KB +15KB #341 +34)
                            (arena=133KB #5)
 
-                  Internal (reserved=495KB +35KB, committed=495KB +35KB)
                            (malloc=463KB +35KB #1429 +8)
                            (mmap: reserved=32KB, committed=32KB)
 
-                     Other (reserved=52KB +36KB, committed=52KB +36KB)
                            (malloc=52KB +36KB #9 +6)
 
-                    Symbol (reserved=6846KB +252KB, committed=6846KB +252KB)
                            (malloc=6294KB +252KB #76359 +3839)
                            (arena=552KB #1)
 
-    Native Memory Tracking (reserved=1727KB +77KB, committed=1727KB +77KB)
                            (malloc=11KB #150 +2)
                            (tracking overhead=1716KB +77KB)
 
-        Shared class space (reserved=17036KB, committed=17036KB)
                            (mmap: reserved=17036KB, committed=17036KB)
 
-               Arena Chunk (reserved=186KB, committed=186KB)
                            (malloc=186KB)
 
-                   Logging (reserved=4KB, committed=4KB)
                            (malloc=4KB #191)
 
-                 Arguments (reserved=18KB, committed=18KB)
                            (malloc=18KB #489)
 
-                    Module (reserved=124KB, committed=124KB)
                            (malloc=124KB #1528 +7)
 
-              Synchronizer (reserved=132KB +3KB, committed=132KB +3KB)
                            (malloc=132KB +3KB #1111 +22)
 
-                 Safepoint (reserved=8KB, committed=8KB)
                            (mmap: reserved=8KB, committed=8KB)

Over time as GC works, we’ll notice an increase and decrease in memory usage. However, if there is an uncontrolled increase in memory usage, then this could be a memory leak issue. Hence, we can identify the memory leak area, like Heap, Thread, Code, Class, etc., from these stats. And if our application needs more memory, we can tune corresponding VM arguments respectively.

随着时间的推移,随着GC的工作,我们会注意到内存使用率的增加和减少。然而,如果内存使用量有一个不受控制的增加,那么这可能是一个内存泄漏问题。因此,我们可以从这些统计数据中识别内存泄漏区域,如HeapThread,Code,Class,等。如果我们的应用程序需要更多的内存,我们可以分别调整相应的虚拟机参数。

If the memory leak is in Heap, we can take a heap dump (as explained earlier) or maybe just tune Xmx. Similarly, if the memory leak is in Thread, we can look for unhandled recursive instructions or tune Xss.

如果内存泄漏是在Heap,我们可以进行堆转储(如前面所解释的),或者也许只是调整Xmx。同样地,如果内存泄漏是在Thread,我们可以寻找未处理的递归指令或调整Xss

6. Conclusion

6.结语

In this article, we’ve covered a utility to diagnose JVM for different scenarios.

在这篇文章中,我们已经介绍了一个针对不同情况诊断JVM的工具。

We also covered the jcmd command and its various usage to get heap dump, thread dump, JFR recording for various performance-related analyses. In the end, we also looked at a way to diagnose a memory leak using jcmd.

我们还介绍了jcmd命令及其各种用法,以获得堆转储、线程转储、JFR记录,用于各种性能相关分析。最后,我们还研究了使用jcmd来诊断内存泄漏的方法。