Series Posts

· 3 min read Posted by Tadeas Kriz

Overriding Gradle Daemon jvmargs

Giving Gradle more memory is a common practice, but it can lead to unexpected behavior. This post explains how to properly set org.gradle.jvmargs.
Diane Picchiottino - https://unsplash.com/photos/white-and-brown-control-panel-TbltzSfSfJ8
Credit: Diane Picchiottino - https://unsplash.com/photos/white-and-brown-control-panel-TbltzSfSfJ8

The Problem

Many of us need to give Gradle more RAM to work with, especially when working with Kotlin Multiplatform. So we open gradle.properties in the project, and add or change the property org.gradle.jvmargs. We slap -Xmx6g -XX:+UseParallelGC (or similar) in and call it a day. It’s only until later we may find weird behavior of our Gradle Daemons, like seeing a “daemon disappeared” message in GitHub Actions. Another side-effect is that -XX:+HeapDumpOnOutOfMemoryError is no longer passed to JVM, making debugging of one-off out of memory errors difficult.

The Cause

To understand why this happens, we need to look at how Gradle usually starts daemons. When a JVM is started, it has default values for JVM args. It’s common that those defaults are replaced with a more domain-specific values. Without the org.gradle.jvmargs, Gradle has its own domain-specific values it passes to the JVM. However, when we provide org.gradle.jvmargs, Gradle no longer provides its defaults, but only whatever we gave it in org.gradle.jvmargs.

The Solution

So what we should do instead, is take the args Gradle would normally use and expand them. Considering that Kotlin requires JDK 8 to work, we can safely copy the arguments from DEFAULT_JVM_8_ARGS. So before we start changing the arguments, it should look something like this:

org.gradle.jvmargs=-Xmx512m -Xms256m -XX:MaxMetaspaceSize=384m -XX:+HeapDumpOnOutOfMemoryError

Note that these values are from Gradle 8.7 and might differ between releases.

After increasing the memory limit and enabling parallel GC, it’d look something like this:

org.gradle.jvmargs=-Xmx6g -Xms256m -XX:MaxMetaspaceSize=384m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC

Notice the -XX:MaxMetaspaceSize argument. If your builds are complex, you might see Gradle daemon running out of metaspace. It will inform you in the build log, so you’ll know to increase the value. Always make sure that argument is present though, as JDK default is unlimited. That can result in Gradle daemon using more and more native memory, eventually running out and crashing.

Ensure seamless Gradle optimization and expert support for your Kotlin Multiplatform projects. Explore how Touchlab’s DevEx Services can provide expert guidance and support.

Conclusion

This issue is way too common and often doesn’t get noticed. It can lay dormant in your project for a long time, until you suddenly start seeing weird behavior. And since it’s this common, it made it’s way to many Open Source projects.

To name a few:

And unfortunately even the official Kotlin Multiplatform Wizard and the templates in its template gallery:

A shoutout to Android documentation, which mentions this issue explicitly in Optimize your build. Gradle documentation mentions the defaults, but doesn’t mention the issue with overriding them.

Let’s hope Gradle finds a way to improve this in the future. Until then, we need to be careful when setting org.gradle.jvmargs.