Introduction

Java's Metaspace stores class metadata and grows dynamically. When applications generate many classes at runtime -- through frameworks like Spring, Hibernate, or bytecode manipulation libraries -- Metaspace can fill up and trigger OutOfMemoryError: Metaspace.

This error is common in large Spring Boot applications, applications using extensive proxy generation, or frameworks that create classes dynamically at runtime.

Symptoms

  • Application crashes with "java.lang.OutOfMemoryError: Metaspace"
  • Error occurs after the application has been running for hours or days
  • Frequent Full GC events with little memory reclaimed before the crash

Common Causes

  • Framework generates too many dynamic classes (proxies, CGLIB, bytecode enhancement)
  • Class loader leak: dynamically loaded classes are never garbage collected
  • MaxMetaspaceSize is set too low or uses default unbounded growth

Step-by-Step Fix

  1. 1.Set appropriate Metaspace limits: Configure Metaspace size to prevent unbounded growth.
  2. 2.```bash
  3. 3.# Set MaxMetaspaceSize to a reasonable limit:
  4. 4.java -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=256m -jar app.jar

# In Docker: ENV JAVA_OPTS="-XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=256m" ```

  1. 1.Diagnose class loader leaks: Use jcmd to analyze Metaspace usage.
  2. 2.```bash
  3. 3.# Get Metaspace usage:
  4. 4.jcmd <pid> VM.metaspace

# Check for class loader leaks: jcmd <pid> VM.classloader_stats

# Generate heap dump for analysis: jmap -dump:live,format=b,file=metaspace.hprof <pid> ```

  1. 1.Enable class unloading to reclaim Metaspace: Allow JVM to unload unused classes.
  2. 2.```bash
  3. 3.# Enable class unloading with G1GC:
  4. 4.java -XX:+UseG1GC -XX:+ClassUnloading -XX:+ClassUnloadingWithConcurrentMark -jar app.jar

# For ZGC (Java 15+): java -XX:+UseZGC -jar app.jar # ZGC does concurrent class unloading by default ```

  1. 1.Monitor Metaspace in production: Set up alerting before Metaspace reaches critical levels.
  2. 2.```java
  3. 3.import java.lang.management.ManagementFactory;
  4. 4.import java.lang.management.MemoryPoolMXBean;

for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) { if ("Metaspace".equals(pool.getName())) { long used = pool.getUsage().getUsed(); long max = pool.getUsage().getMax(); System.out.printf("Metaspace: %.1f MB / %.1f MB%n", used / 1024.0 / 1024.0, max / 1024.0 / 1024.0); } } ```

Prevention

  • Set -XX:MaxMetaspaceSize to prevent unbounded growth in production
  • Use G1GC with class unloading enabled for dynamic class-heavy applications
  • Monitor Metaspace usage with JMX and set alerts at 80% capacity
  • Profile application with JFR (Java Flight Recorder) to identify excessive class generation