1. Overview

It’s possible to tune the HotSpot JVM with a variety of tuning flags. As there are hundreds of such flags, keeping track of them and their default values can be a little daunting.

In this tutorial, we’re going to introduce a few ways to discover such tuning flags and learn how to work with them.

2. Overview of Java Options

The java command supports a wide variety of flags falling into the following categories:

  • Standard options that are guaranteed to be supported by all JVM implementations out there. Usually, these options are used for everyday actions such as –classpath, -cp, –version, and so on
  • Extra options that are not supported by all JVM implementations and are usually subject to change. These options start with -X

Please note that we shouldn’t use these extra options casually. Moreover, some of those additional options are more advanced and begin with -XX. 

Throughout this article, we’re going to focus on more advanced -XX flags.

3. JVM Tuning Flags

To list the global JVM tuning flags, we can enable the PrintFlagsFinal flag as follows:

>> java -XX:+PrintFlagsFinal -version
[Global flags]
    uintx CodeCacheExpansionSize                   = 65536                                  {pd product} {default}
     bool CompactStrings                           = true                                   {pd product} {default}
     bool DoEscapeAnalysis                         = true                                   {C2 product} {default}
   double G1ConcMarkStepDurationMillis             = 10.000000                                 {product} {default}
   size_t G1HeapRegionSize                         = 1048576                                   {product} {ergonomic}
    uintx MaxHeapFreeRatio                         = 70                                     {manageable} {default}

// truncated
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)

As shown above, some flags have default values for this particular JVM version.

3.2. Experimental Flags

To also see experimental options, we should add the UnlockExperimentalVMOptions flag:

>> java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version | wc -l
809

3.3. JVMCI Flags

As of Java 9, the JVM compiler interface or JVMCI enables us to use a compiler written in Java, such as Graal, as a dynamic compiler.

To see options related to JVMCI, we should add a few more flags and also even enable the JVMCI:

>> java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions \
>> -XX:+JVMCIPrintProperties -XX:+EnableJVMCI -XX:+PrintFlagsFinal  -version | wc -l
1516

Most of the time, however, using global, diagnostic, and experimental options should suffice and will help us to find the flag we have in mind.

3.4. Putting It All Together

These combinations of options can help us to find a tuning flag, especially when we don’t remember the exact name. For instance, to find the tuning flag related to soft references in Java:

>> alias jflags="java -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal  -version"
>> jflags | grep Soft
size_t SoftMaxHeapSize                          = 4294967296                             {manageable} {ergonomic}
intx SoftRefLRUPolicyMSPerMB                    = 1000                                   {product} {default}

From the result, we can easily guess that SoftRefLRUPolicyMSPerMB is the flag we’re looking for.

4. Different Types of Flags

In the previous section, we glossed over an important subject: the flag types. Let’s take another look at the java -XX:+PrintFlagsFinal -version output:

[Global flags]
    uintx CodeCacheExpansionSize                   = 65536                                  {pd product} {default}
     bool CompactStrings                           = true                                   {pd product} {default}
     bool DoEscapeAnalysis                         = true                                   {C2 product} {default}
   double G1ConcMarkStepDurationMillis             = 10.000000                                 {product} {default}
   size_t G1HeapRegionSize                         = 1048576                                   {product} {ergonomic}
    uintx MaxHeapFreeRatio                         = 70                                     {manageable} {default}
// truncated

As shown above, each flag has a specific type.

Boolean options are used to either enable or disable a feature. Such options don’t require a value. To enable them, we just have to put a plus sign before the option name:

-XX:+PrintFlagsFinal

On the contrary, to disable them, we have to add a minus sign before their name:

-XX:-RestrictContended

Other flag types need an argument value. It’s possible to separate the value from the option name by a space, a colon, an equal sign, or the argument may directly follow the option name (the exact syntax differs for each option):

-XX:ObjectAlignmentInBytes=16 -Xms5g -Xlog:gc

5. Documentation and Source Code

Finding the right flag name is one thing. Finding what that particular flag is doing under the hood is another story.

One way to find out these sorts of details is by looking at the documentation. For instance, the documentation for the java command in the JDK tools specification section is a great place to start.

Sometimes, no amount of documentation can beat the source code. Therefore, if we have the name of a particular flag, then we can explore the JVM source code to find out what’s going on.

For instance, we can check out the HotSpot JVM’s source code from GitHub or even their Mercurial repository and then:

>> git clone [email protected]:openjdk/jdk14u.git openjdk
>> cd openjdk/src/hotspot
>> grep -FR 'PrintFlagsFinal' .
./share/runtime/globals.hpp:  product(bool, PrintFlagsFinal, false,                                   
./share/runtime/init.cpp:  if (PrintFlagsFinal || PrintFlagsRanges) {

Here we’re looking for all files containing the PrintFlagsFinal string. After finding the responsible files, we can look around and see how that specific flag works.

6. Conclusion

In this article, we saw how we could find almost all available JVM tuning flags and also learned a few tricks to work with them more effectively.