Skip to main content

Gradle memory fine-tuning guide

Investigating memory allocations of Gradle builds

Updated this week

Issue:

I got the following error message. How can I optimize Gadle memory allocations?

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "pool-1-thread-1"

Possible solutions:

JVM Garbage Collector

Use JVM Garbage Collector to automatically free up memory space and keep the used heap below the allocation. The memory management can be fine-tuned with various flags such as:

  • -Xms: initial heap size

  • -Xmx: maximum heap size

  • -XX:+UseParallelGC: JVM has multiple garbage collector algorithms with different tradeoffs. This flag allows overriding the default GC algorithm.

  • -XX:MaxMetaspaceSize: metaspace is the memory area that stores metadata about loaded classes. By default, this is unlimited and can grow too large relative to the physical memory of the system.

    JVM ships with multiple garbage collector implementations that can be swapped with the -XX flag. Use the one that works best for your workload. The JVM docs include a guide for selecting the right garbage collector (for Gradle-like workloads, the Parallel GC is the optimal choice).

Memory usage tips during a Gradle invocation

In Gradle projects, the previously shown JVM flags can be defined in the gradle.properties file like so:

org.gradle.jvmargs=-Xmx4G -Xms512M -XX:MaxMetaspaceSize=1g -XX:+UseParallelGC

When you execute ./gradlew assembleDebug, the first process that starts is the Gradle Daemon, and this is the main JVM process that does most of the heavy lifting. Gradle tasks often run in this JVM instance, so it’s important to allocate enough memory to it. Gradle uses a 512 MB max heap size (-Xmx) by default, which causes the out-of-memory crashes in Android projects. Android docs also suggest increasing this limit to avoid memory-related crashes and wasted CPU cycles because of garbage collection.

There is one more JVM process in a typical Android build: the Kotlin compiler daemon. This is a separate process spawned by the first Kotlin compile task and executes all compile tasks during the Gradle invocation. By default, this JVM instance inherits the memory settings of the org.gradle.jvmargs set in gradle.properties, so you should calculate with two JVM instances when tweaking the JVM args of an Android project.

There are also other memory-hungry Gradle tasks in a typical Android project, for example:

Did this answer your question?