skip to content

What is metaspace, why do long-lived Gradle daemons hit 'OutOfMemoryError: Metaspace', and how does MaxMetaspaceSize fit into the fix?

level: seniorimportance: should knowfreq 38%

answer

  1. metaspace = native class-metadata region (post-PermGen)
  2. reclaimed only when classloader GC'd
  3. long-lived daemon surfaces classloader leaks
  4. MaxMetaspaceSize bounds, doesn't fix
  5. separate from -Xmx; recycle daemon to mitigate

basics

~20 s

Metaspace is native memory holding class metadata. A reused daemon loads classes across many builds; leaked classloaders accumulate metadata until 'OutOfMemoryError: Metaspace'. Set -XX:MaxMetaspaceSize to a sane cap (and fail fast with a dump) rather than leaving it unbounded.

solid answer

~50 s

**Metaspace** is the native (off-heap) region where the JVM stores class metadata — one entry per loaded class. The Gradle daemon is **long-lived and reused** across builds, and each build can load plugin and compiler classes through new classloaders. If a classloader is retained (a leak — e.g. a plugin or build-script class held by a static reference), its classes can't be unloaded and metaspace grows build after build until `OutOfMemoryError: Metaspace`. Setting `-XX:MaxMetaspaceSize` in `org.gradle.jvmargs` does two things: it bounds the growth (so a leaking daemon fails predictably instead of consuming all native memory) and, with `-XX:+HeapDumpOnOutOfMemoryError`, lets you capture the leaked classloaders for diagnosis. The real fix is usually to stop the leak (fix the offending plugin / static reference) — but bounding metaspace and letting the daemon recycle (e.g. via idle expiry or `--stop`) is the pragmatic mitigation. Note metaspace is **separate** from `-Xmx`; raising the heap does nothing for a metaspace OOM.

code

toml · 3 lines
toml
# gradle.properties
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m \
  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=build/heapdumps

go deeper

for a junior

Know metaspace holds class metadata and is separate from heap; -Xmx won't fix a metaspace OOM.

for a middle

Explain that classloader retention prevents unloading and that the long-lived daemon surfaces leaks.

for a senior

Connect MaxMetaspaceSize bounding + heap dumps + daemon recycling to diagnosing and mitigating classloader leaks.

for a principal

Set policy: cap metaspace across repos, capture dumps in CI, and drive plugin owners to fix classloader leaks rather than masking them with unbounded metaspace.

## What metaspace is Since Java 8, class metadata (the runtime representation of loaded classes — method tables, constant pools, etc.) lives in **metaspace**, a region of **native** memory (it replaced the old PermGen, which was inside the heap). Crucially, metaspace is **not** part of the Java heap, so `-Xmx` has no effect on it. By default metaspace is **unbounded** (limited only by available native memory) unless you set `-XX:MaxMetaspaceSize`. ## Why class metadata accumulates Class metadata can only be reclaimed when the **classloader** that loaded those classes becomes unreachable and is garbage-collected. As long as a classloader is alive, every class it loaded stays in metaspace. ## Why the Gradle daemon is exposed to this The daemon is a **persistent process reused across many builds**. Each build may: - compile and load **build-script** classes, - load **plugin** classes (often through fresh classloaders), - load classes for in-process tools. If any of those classloaders is **retained** — a static field somewhere holds a reference, a listener isn't deregistered, a cache keeps the loader alive — the classes never unload. Over dozens of builds in the same daemon, metaspace creeps up until `OutOfMemoryError: Metaspace`. This is the classic 'classloader leak', and the daemon's longevity is exactly what surfaces it (a fresh JVM per build would mask it). ## Where MaxMetaspaceSize fits ```properties org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m \ -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=build/heapdumps ``` `-XX:MaxMetaspaceSize` does NOT fix a leak — it **bounds** it. Benefits: 1. **Predictable failure.** Without a cap, a leaking daemon can grow metaspace until it exhausts native memory and gets OOM-killed by the OS/container with a confusing error. A cap makes it fail with a clear `Metaspace` OOM at a known threshold. 2. **Diagnosis.** Combined with a heap dump, you can see the retained classloaders and which build/plugin loaded them. 3. **Protects the box.** It keeps a runaway daemon from starving everything else on a shared CI machine. ## The actual fixes - **Stop the leak** at its source (fix the plugin / static reference). This is the durable fix. - **Recycle the daemon** so leaked metadata is reclaimed periodically: the daemon expires on idle timeout, and you can force `./gradlew --stop`. A daemon that restarts regularly never accumulates enough to OOM. - **Size metaspace** to comfortably hold a clean build's classes plus headroom, but not so large it just delays an obvious leak. ## Key distinction to state in an interview A **heap-space** OOM and a **metaspace** OOM are different failures with different fixes: heap-space -> `-Xmx`; metaspace -> `-XX:MaxMetaspaceSize` + leak hunting. Confusing the two is a common mistake.

  • Why doesn't raising -Xmx help an 'OutOfMemoryError: Metaspace'?
    Metaspace is native memory, separate from the Java heap. -Xmx sizes the heap only; the metaspace cap is -XX:MaxMetaspaceSize, and the underlying issue is usually retained classloaders.
  • Capping MaxMetaspaceSize made the daemon OOM faster. Is that a problem?
    Faster failure with a clear Metaspace error plus a heap dump is preferable to silently exhausting native memory and getting OOM-killed. Use the dump to find the leaked classloaders, fix the leak, and/or let the daemon recycle.

Metaspace is like a library's card catalog: every loaded class gets a card. The daemon is a librarian who never goes home, so if some department (a leaky classloader) keeps requesting cards and never returns them, the catalog overflows — no matter how big the reading room (heap) is.

saying these in an interview costs you the question

  • Treating a metaspace OOM by raising -Xmx — wrong region entirely.
  • Believing MaxMetaspaceSize fixes a leak; it only bounds growth and forces a clean failure.
  • Assuming class metadata frees up while the loading classloader is still referenced.

context