Jcmd is a recommended tool from JDK 7 onwards for enhanced JVM diagnostics with no or minimum performance overhead.
Jcmd is a utility that sends diagnostic command requests to a running JVM. It must be used on the same machine on which JVM is running. Additional details are available in its documentation.
The command syntax is as follows:
The available <options>
are:
-J
: supplies arguments to the Java VM that is running the jcmd command. You can use multiple-J
options, for example:jcmd -J-Xmx10m -J-Dcom.ibm.tools.attach.enable=yes
-h
: prints the jcmd help<vmid>
is the Attach API virtual machine identifier for the Java™ VM process. This ID is often, but not always, the same as the operating system process ID. One example where the ID might be different is if you specified the system property -Dcom.ibm.tools.attach.id when you started the process. You can use the jps command to find the VMID.
The available arguments are:
help
: shows the diagnostic commands that are available for this VM. This list of commands can vary between VMs.help <command>
: shows help information for the specified diagnostic command<command> [<command_arguments>]
: runs the specified diagnostic command, with optional command arguments
Usage
Getting the 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:
Here, we can see the PID of our running application is 18.
Get List of Possible jcmd Usage
Let’s find out possible options available with the jcmd PID help command to start with:
The available diagnostic commands may be different in different versions of HotSpot VM.
jcmd Commands
Let’s explore some of the most useful jcmd command options to diagnose our running JVM.
VM.version
This is to get JVM basic details as shown below:
Here we can see that we are using OpenJDK 11 for our sample application.
VM.system_properties
This will print all the system properties set for our VM. There can be several hundred lines of information displayed:
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:
Similarly, other commands, like VM.command_line, VM.uptime, VM.dynlibs, also provide other basic and useful details about various other properties used.
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.
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:
Detailed discussion on capturing a thread dump using other options can be found here.
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:
However, if this doesn’t give a clear picture, we can get a heap dump. Let’s look at it next.
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:
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.
JFR Command Options
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.
JFR (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.
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
.
Now let’s generate a JFR file using the jcmd command as below:
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.
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.
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.
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.
This will give us a new PID to diagnose:
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.
In addition, the reserved can give an estimation of the memory required for our application. And the committed shows the minimum allocated memory.
Diagnose Memory Leak
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.
Let’s first baseline the JVM memory usage as below:
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:
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.
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.
reference
- https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jcmd.html
- https://sheerheart.tistory.com/entry/JVM-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-%EB%B0%8F-%EB%B6%84%EC%84%9D%EC%9D%84-%EC%9C%84%ED%95%9C-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%93%A4#google_vignette
- https://www.ibm.com/docs/en/semeru-runtime-ce-z/11?topic=tools-java-command-jcmd-tool
- https://bell-sw.com/announcements/2021/10/14/jcmd-everywhere-locally-containerized-and-remotely/