Clojure AOT compilation OutOfMemoryError in CI
AOT compilation (during lein uberjar or a tools.build compile-clj) loads and compiles every namespace, which is memory-heavy. On a constrained runner the compiling JVM can exceed its heap and throw OutOfMemoryError.
What this error means
An uberjar or AOT build fails with "java.lang.OutOfMemoryError: Java heap space" (or GC overhead limit exceeded) partway through compiling namespaces.
Compiling myapp.core
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at clojure.lang.Compiler ...Common causes
The compiling JVM heap is too small
AOT holds many loaded classes in memory; a default or low -Xmx on the runner is exhausted before compilation finishes.
A large namespace graph compiled at once
A big app AOT-compiling all namespaces (rather than just the entry) needs more heap than the runner allots.
How to fix it
Raise the JVM heap for the build
Give the compiling JVM more heap through the tool-specific options variable.
# Leiningen
export JVM_OPTS="-Xmx4g"
lein uberjar
# tools.build
clojure -J-Xmx4g -T:build uberLimit AOT to the entry namespace
Skip AOT on the rest of the tree so only what needs compiling is compiled.
(defproject myapp "0.1.0"
:main ^:skip-aot myapp.core
:aot [myapp.core])How to prevent it
- Set an explicit
-Xmxfor AOT/uberjar builds on constrained runners. - AOT only the namespaces that must be compiled.
- Use a runner with enough memory for the compile step.