Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In addition to typical development utilities such as compiler and runtime, each JDK release is shipped with a myriad of other tools. Some of these tools can help us to gain valuable insights into our running applications.

In this article, we're going to see how we can use such tools to find out more about the GC algorithm used by a particular JVM instance.

2. Sample Application

Throughout this article, we're going to use a very simple application:

public class App {
    public static void main(String[] args) throws IOException {
        System.out.println("Waiting for stdin");
        int read = System.in.read();
        System.out.println("I'm done: " + read);
    }
}

Obviously, this app waits and keeps running until it receives something from the standard input. This suspension helps us to mimic the behavior of long-running JVM applications.

In order to use this app, we have to compile the App.java file with javac and then run it using the java tool.

3. Finding the JVM Process

To find the GC used by a JVM process, first, we should identify the process id of that particular JVM instance. Let's say that we ran our app with the following command:

>> java App
Waiting for stdin

If we have JDK installed, the best way to find the process id of JVM instances is to use the jps tool. For instance:

>> jps -l
69569 
48347 App
48351 jdk.jcmd/sun.tools.jps.Jps

As shown above, there are three JVM instances running on the system. Obviously, the description of the second JVM instance (“App”) matches our application name. Therefore, the process id we're looking for is 48347.

In addition to jps, we can always use other general utilities to filter out running processes. For instance, the famous ps tool from the procps package will work as well:

>> ps -ef | grep java
502 48347 36213   0  1:28AM ttys037    0:00.28 java App

However, the jps is way simpler to use and requires less filtering.

4. Used GC

Now that we know how to find the process id, let's find the GC algorithm used by JVM applications that are already running.

4.1. Java 8 and Earlier

If we're on Java 8, we can use the jmap utility to print the heap summary, heap histogram, or even generate a heap dump. In order to find the GC algorithm, we can use the -heap option as:

>> jmap -heap <pid>

So in our particular case, we're using the CMS GC:

>> jmap -heap 48347 | grep GC
Concurrent Mark-Sweep GC

For other GC algorithms, the output is almost the same:

>> jmap -heap 48347 | grep GC
Parallel GC with 8 thread(s)

4.2. Java 9+: jhsdb jmap

As of Java 9, we can use the jhsdb jmap combination to print some information about the JVM heap. More specifically, this particular command would be equivalent to the previous one:

>> jhsdb jmap --heap --pid <pid>

For instance, our app is running with G1GC now:

>> jhsdb jmap --heap --pid 48347 | grep GC
Garbage-First (G1) GC with 8 thread(s)

4.3. Java 9+: jcmd

In modern JVMs, the jcmd command is pretty versatile. For instance, we can use it to get some general info about the heap:

>> jcmd <pid> VM.info

So if we pass our app's process id, we can see that this JVM instance is using Serial GC:

>> jcmd 48347 VM.info | grep gc
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, serial gc, bsd-amd64)
// omitted

The output is similar for G1 or ZGC:

// ZGC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, z gc, bsd-amd64)
// G1GC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, g1 gc, bsd-amd64)

With a little bit of grep magic, we can also remove all those noises and just get the GC name:

>> jcmd 48347 VM.info | grep -ohE "[^\s^,]+\sgc"
g1 gc

4.4. Command Line Arguments

Sometimes, we (or someone else) explicitly specify the GC algorithm while launching the JVM application. For instance, we're opting to use ZGC here:

>> java -XX:+UseZGC App

In such cases, there are much simpler ways to find the used GC. Basically, all we have to do is to somehow find the command that the application has been executed with.

For example, on UNIX-based platforms, we can use the ps command again:

>> ps -p 48347 -o command=
java -XX:+UseZGC App

From the above output, it's obvious that the JVM is using ZGC. Similarly, the jcmd command also can print the command line arguments:

>> jcmd 48347 VM.flags
84020:
-XX:CICompilerCount=4 -XX:-UseCompressedOops -XX:-UseNUMA -XX:-UseNUMAInterleaving -XX:+UseZGC // omitted

Surprisingly, as shown above, this command will print both implicit and explicit arguments and tunables. So even if we don't specify the GC algorithm explicitly, it'll show the selected and default one:

>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseG1GC

And even more surprising, this will work on Java 8 as well:

>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseParallelGC

5. Conclusion

In this article, we saw different approaches to find the GC algorithm used by a particular JVM instance. Some of the mentioned approaches were tied to specific Java versions, and some were portable.

Moreover, we saw a couple of ways to find the process id, which is always needed.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
guest
0 Comments
Inline Feedbacks
View all comments