jps

You may have used the ps command to print information about all running processes. JPS in the JDK. It follows the same concept: it prints information about all running Java processes.

By default, JPS output includes the process ID of the Java process and the main class name. We can also print additional information by appending parameters. For example, -l prints the module name and the package name; -v prints the parameters passed to the Java virtual machine

(such as – XX: + UnlockExperimentalVMOptions – XX: + UseZGC); -m prints the parameters passed to the main class.

An example is as follows

Note that the JPS command (and jstat below) will not be able to detect a Java process if it turns off the UsePerfData parameter that is enabled by default (using -xx: -useperfData).

Once we have the Process ID of the Java process, we can invoke the monitoring and diagnostic tools described next.

jstat

The jstat command can be used to print performance data for the target Java process. It contains multiple sliver commands, as follows:

In these subcommands, -class prints class-loading data, and -compiler and -printcompilation print just-in-time compilation data. The rest are subcommands prefixed with -gc that print garbage collection-related data.

By default, jstat prints only one-time data. We can configure it to print every once in a while until the target Java process terminates, or the maximum number of times we have configured it to print. Specific examples are as follows:

When monitoring Java processes in the local environment, VMID can be simply understood as PID. If you need to monitor Java processes in remote environments, you can refer to the jstat help documentation.

In the example above, process 22126 is a Java process that uses the CMS garbage collector. We use the -gc subcommand of jstat to print the process garbage collection data. The last 1s 4 of the command is printed every second for four times.

In the output of the -gc subcommand, the first four columns are Capacity and Utility, respectively, for the two Survivor zones. We can see that the two Survivor zones have the same capacity, and there is always one Survivor zone with zero memory usage.

When using the default G1 GC, the output has some other characteristics:

In the example above, Jstat prints the garbage collection every 1s and repeats.

You may have noticed that S0C and S0U are always 0, and that the capacity of another Survivor zone (S1C) may drop to 0.

This is because, when using the G1 GC, the Java virtual machine no longer sets memory boundaries for the Eden region, Survivor region, and old age region, but instead divides the heap into several equal-length memory regions.

Each memory region can be either Eden region, Survivor region, or old age region, and can switch back and forth between different region types.

In other words, logically we only have one Survivor zone. When migrating data in a Survivor region (i.e., migrating GC), we simply apply for one or more additional memory regions as new Survivor regions.

Therefore, the Java virtual machine decides to store the total capacity and usage of all Survivor memory regions in S1C and S1U when using G1 GC, while S0C and S0U are set to 0.

When garbage collection occurs, all objects in the Survivor memory region of the Java virtual machine may be collected or promoted.

In this case, the Java virtual machine reclaims the memory area and marks it as allocatable. As a result, there may be no Survivor regions in the heap at all, and the corresponding S1C and S1U will be 0.

Jstat also has a very useful parameter -t, which prints the start time of the target Java process before each line of data. For example, in the following example, the first column represents that the Java process has been started for 10.7 seconds.

We can compare the Java process startup time with the total GC time (GCT column), or the time between the two measurements and the increment of the total GC time to get a percentage of the elapsed time.

If the ratio is more than 20%, it indicates that the current heap pressure is high; If the ratio exceeds 90%, there is little free space in the heap and an OOM exception could be thrown at any time.

Jstat can also be used to determine if there is a memory leak. In a long-running Java program, we can run the jstat command to get multiple rows of performance data in a row and take the minimum value of the OU columns (that is, old memory used) in those rows.

We then repeat the above operation at long intervals to obtain the minimum of multiple OU values. If these values show an upward trend, it indicates that the Java program’s memory usage is increasing in the past, which means that there are more objects that cannot be reclaimed and therefore a memory leak is likely.

For columns not covered above (or the output of other subcommands), you can refer to the help documentation for details. As for The CGC and CGCT left out of The documentation, they represent The number and time of concurrent GC stop-the-world, respectively.

jmap

In this case, we can ask the jmap command (help document) to analyze the objects in the Java virtual machine heap.

Jmap also includes multiple sliver commands.

  • -clstats, this subcommand will print information about the loaded class.
  • -finalizerinfo, this subcommand will print all objects to finalize.
  • -histo, the subcommand counts the number of instances of each class and the memory usage in the order of the largest memory usage. In addition, -histo:live counts only live objects in the heap.
  • -dump: exports the snapshot of the Java VM heap. Also, -dump:live saves only live objects in the heap. We usually use jmap -dump:live,format=b,file=filename.bin to export all the live objects in the heap to a file.

Here I have pasted the output of the -histo subcommand:

Since JMap will access all objects in the heap, in order to ensure that it is not disturbed by application threads in the process, JMap needs to use a safety point mechanism that allows all threads to stay in state without changing the data in the heap.

That is, ** the heap snapshot exported by JMAP must be at a safe point location. This can lead to biased results based on the heap snapshot. ** For example, if some objects in the compile-generated machine code have a lifetime between two safety points, the :live option will not be able to detect those objects.

In addition, if a thread fails to reach a safe point for a long time, JMap will wait forever. Jstat in the previous section is different. This is because the garbage collector actively stores the summary data required by Jstat in a fixed location, whereas Jstat simply reads it.

You can reproduce this long wait by using the following procedure:

Jmap (and then JINFO, JStack, and JCMD) rely on the Attach API of the Java virtual machine and can only monitor local Java processes.

Once the Java VM parameter DisableAttachMechanism is enabled (using -xx :+DisableAttachMechanism), the command based on the Attach API cannot be executed. On the other hand, if you don’t want to be monitored by other processes, you need to turn this parameter on.

jinfo

The jinfo command can be used to view the parameters of the target Java process, such as -x (jVM_args in the output), -xx (VM Flags in the output), And the -d parameter (System Properties in the output) that can be obtained at the Java level via System.getProperty.

An example is as follows:

Jinfo can also be used to modify the “Manageable” virtual machine parameter of the target Java process.

For example, we can use the jinfo-flag +HeapDumpAfterFullGC < PID > command to turn on the HeapDumpAfterFullGC parameter for the Java process specified by < PID >.

You can view the other “manageable” VM parameters by running the following command:

jstack

The jstack command can be used to print the stack traces of individual threads in the target Java process, as well as the locks held by those threads.

One application of JStack is deadlock detection. Here I use JStack to get stack information for a Java program that has been deadlocked. The output is as follows:

As you can see, jStack not only prints the thread’s stack trace, BLOCKED thread state, locked thread… Waiting to lock… And it will also analyze specific deadlocks.

jcmd

You can also use the JCMD command directly instead of all the previous commands except jstat.

conclusion

A command-line tool in the JDK for monitoring and diagnostics. There are mainly the following ways.

  • JPS will print all running Java processes.
  • Jstat allows the user to view information about the class loading, just-in-time compilation, and garbage collection of the target Java process. It is often used to detect garbage collection problems as well as memory leaks.
  • Jmap allows the user to count the Java objects stored in the heap of the target Java process and export them to binary files.
  • Jinfo will print the configuration parameters of the target Java process and can change the parameters of Manageabe.
  • Jstack prints the stack trace, thread status, lock status, and other information for each thread in the target Java process. It will also automatically detect deadlocks.
  • JCMD is a Swiss Army knife that can be used to do all of the previous commands except jstat.