ZGC | Using -XX:SoftMaxHeapSize

In JDK 13 we introduced a new JVM option called -XX:SoftMaxHeapSize=<size>. ZGC is so far the only garbage collector in HotSpot that supports this options, but work to also support it in G1 is ongoing. Since this option is relatively new, and perhaps not yet widely known, I thought I’d write a few words about when and how to use it.

When is -XX:SoftMaxHeapSize useful?

As the name implies, this new option sets a soft limit on how large the Java heap can grow. When set, the GC will strive to not grow the heap beyond the soft max heap size. But, the GC is still allowed to grow the heap beyond the specified size if it really needs to, like when the only other alternatives left is to stall an allocation or throw an OutOfMemoryError.

There are different use cases where setting a soft max heap size can be useful. For example:

  • When you want to keep the heap footprint down, while maintaining the capability to handle a temporary increase in heap space requirement.
  • Or when you want to play it safe, to increase confidence that you will not run into an allocation stall or have an OutOfMemoryError because of an unforeseen increase in allocation rate or live-set size.

Let’s make up an example, to illustrate the first use case listed above. Pretend that your workload under normal conditions needs 2G of heap to run well. But once in a while you see workload spikes (maybe you’re providing a service that attracts a lot more users a certain day of the week/month, or something similar). With this increase in workload your application now needs, say, 5G to run well. To deal with this situation you would normally set the max heap size (-Xmx) to 5G to cover for the occasional workload spikes. However, that also means you will be wasting 3G of memory 98% (or something) of the time when it’s not needed, since those unused 3G will still be tied up in the Java heap. This is where a soft max heap size can come in handy, which together with ZGC’s ability to uncommit unused memory allows you to have your cake and eat it too. Set the max heap size to the max amount of heap your application will ever need (-Xmx5G in this case), and set the soft max heap size to what your application needs under normal conditions (-XX:SoftMaxHeapSize=2G in this case). You’re now covered to handle those workload spikes nicely, without always wasting 3G. Once the workload spike has passed and the need for memory drops down to normal again, ZGC will shrink the heap and continue to do it’s best to honor the -XX:SoftMaxHeapSize setting. When those extra 3G of heap have been sitting unused for a while, ZGC will uncommit that memory, returning it to the operating system for other processes (or the disk cache, or something else) to use.

Change -XX:SoftMaxHeapSize at runtime

The SoftMaxHeapSize option is also manageable, meaning it can be changed at runtime without having to restart the JVM. You can change a manageable JVM option at runtime using the HotSpotDiagnosticMXBean or jcmd, like this.

jcmd <pid> VM.set_flag SoftMaxHeapSize <size>

The soft max heap size can not be set to a value greater than the max heap size. When not set on the command-line, the soft max heap size will default to the same value as the max heap size.

Uncommit aggressiveness

How aggressively ZGC uncommits unused memory can be controlled with -XX:ZUncommitDelay=<seconds>, which defaults to 300 seconds (5 minutes). In other words, by default, memory must have been unused for 5 minutes before it’s eligible for uncommit. Note that committing and uncommitting memory are relatively expensive operations. A too aggressive (short) uncommit delay will just cause uncommitted memory to very soon be committed again, which can be a waste of CPU cycles and might in the end also impact the overall performance of your application.

TL;DR

Using -XX:SoftMaxHeapSize=2G -Xmx5G will tell ZGC to keep the max heap usage at 2G, but it’s allowed to grow to 5G if it otherwise would have resulted in an allocation stall or an OutOfMemoryError. This is useful when you want to keep the memory footprint down, while maintaining the capability to handle a temporary increase in heap space requirement.

For more information on ZGC, please see the OpenJDK Wiki, the GC section on Inside Java, or this blog.