Solving OutOfMemoryError During Quarkus Native Image Compilation
Learn how to diagnose and fix OutOfMemoryErrors during Quarkus native image compilation by optimizing GraalVM heap settings and CI/CD resource allocation.
19 Oct 2025, 20:57 UTC

The Problem: Build-Time Memory Exhaustion
When building a Quarkus application for GraalVM native images, you may encounter a java.lang.OutOfMemoryError: GC overhead limit exceeded or a system-level process kill (OOM Killer) during the native-image phase. This happens because native compilation is not a standard compilation; it is a static analysis process that maps every reachable code path in your application and its dependencies to create a standalone binary.
The critical takeaway: The memory required to build a native image is significantly higher than the memory required to run the resulting binary. If your build environment is constrained (such as a CI/CD runner with 4GB or 8GB of RAM), the GraalVM builder will likely crash before the image is produced.
Diagnostic Matrix
Use this table to identify the specific type of memory failure based on your build logs.
| Symptom | Likely Cause | Diagnostic Indicator |
|---|---|---|
java.lang.OutOfMemoryError: GC overhead limit exceeded |
Insufficient JVM Heap for the builder | Logs show repeated Garbage Collection attempts during the "Analysis" phase. |
Exit code 137 or Killed |
OS-level OOM Killer | No Java stack trace; process terminates abruptly. Check dmesg or system logs. |
| Extremely slow build (hours) with high disk I/O | System Swapping | top or htop shows high %swap usage and low CPU utilization. |
Step-by-Step Memory Resolution
1. Verify Current Allocation
First, determine how much memory the native-image process is actually attempting to use. Run your build with the -Xlog or verbose flags enabled for the Quarkus plugin.
- Maven:
mvn package -Pnative -Dquarkus.native.additional-build-args="-verbose" - Gradle:
./gradlew build -Pnative -Dquarkus.native.additional-build-args="-verbose"
Check the logs for the -Xmx flag passed to the native-image tool. If it is missing or set to a value lower than 4GB, the builder is likely using a default that is insufficient for your dependency graph.
2. Increase the Builder Heap
If you have physical RAM available, increase the maximum heap size for the GraalVM builder. This is passed via the quarkus.native.additional-build-args property. This setting affects the compiler, not the final application binary.
Configuration Example:
# For Maven (pom.xml or command line)
-Dquarkus.native.additional-build-args="-Xmx8g"
Risk: Do not set -Xmx to the total physical RAM of your machine. Leave at least 2GB for the OS and other background processes to avoid triggering system swap, which can slow the build by 10x.
3. Optimize Dependency Footprint
If increasing the heap is not an option (e.g., restricted CI runner), you must reduce the amount of code GraalVM has to analyze. Large numbers of reflection configurations or unused heavy libraries increase the memory footprint during the analysis phase.
- Remove unused dependencies: Audit your
pom.xmlorbuild.gradlefor libraries that are not strictly necessary. - Refine Reflection: If using
@RegisterForReflection, apply it only to the specific classes needed rather than entire packages.
4. Configure CI/CD Resource Limits
In GitHub Actions, GitLab CI, or Jenkins, the container memory limit is often the bottleneck. If the container limit is 8GB and you set -Xmx8g, the OS will kill the process because the native-image tool also requires off-heap memory for its internal data structures.
Recommended Ratio: Set the container memory limit to roughly 1.5x the -Xmx value. For example, if you set -Xmx6g, ensure your CI runner has at least 9GB of available RAM.
Verification and Results
To verify the fix, monitor the build using htop (Linux) or Activity Monitor (macOS) on a local machine. Look for the native-image process. The memory usage should plateau during the "Analysis" phase without triggering a crash or excessive swapping.
Rollback Procedure
Since these changes are configuration-based, rollback involves removing the -Xmx argument from your build command or pom.xml/build.gradle file to return to the GraalVM defaults.
Escalation Criteria
If the build still fails after allocating 16GB+ of RAM, the issue is likely not a simple heap shortage but one of the following:
- Circular Dependency Loops: Extremely complex generic types or deep inheritance trees can cause the static analysis to explode in memory usage.
- Incorrect GraalVM Version: Ensure the GraalVM version matches the Quarkus version requirements. Mismatched versions can lead to inefficient memory usage during compilation.
- Insufficient Disk Space: Native compilation creates large temporary files. Ensure you have at least 5GB of free space in the
/tmpdirectory or the projecttargetfolder.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.