March 26, 2019
share:

What is Metaspace?

The OpenJDK uses Metaspace to store its class metadata. It can contribute a large part to the non-Java-heap memory footprint of a Java VM process.

We explain what it is and why we need it. A short, quick and hopefully easy read without diving too deep into VM internals. Should be digestible by everyone.


Note: JDK version dependencies: Metaspace implementation changed quite a bit since JDK 8. In these articles, when not explicitly stated differently, we talk about JDK 11.

Metaspace is memory the VM uses to store class metadata.

Class metadata are the runtime representation of java classes within a JVM process - basically any information the JVM needs to work with a Java class. That includes, but is not limited to, runtime representation of data from the JVM class file format.

Examples:

  • the “Klass” structure - the VM-internal representation of runtime state of a java class. This includes both vtable and itable.
  • Method metadata - runtime equivalent of the method_info in the class file, containing things like the bytecode, exception table, constants, etc.
  • The constant pool
  • Annotations
  • method counters collected at runtime as a base for JIT decisions
  • etc.

Note: We will examine all this in more detail in Part 4 of this series.

Another good introduction into class metadata in hotspot is Andrew Dinn’s talk at FOSDEM 18.

Even though java.lang.Class is a java object living in Java heap, the class metadata itself are no Java objects and do not live in the Java heap. They live in a native memory region outside of the Java heap. That region is called Metaspace.

When does Metaspace get allocated?

Allocation from Metaspace is coupled to class loading.

When a class is loaded and its runtime representation in the JVM is being prepared, Metaspace is allocated by its class loader to store the class’ metadata.

Metadata lifecycle - Allocation

When does Metaspace get released?

The allocated Metaspace for a class is owned by its class loader 1. It is only released when that class loader itself is unloaded, not before.

That in turn happens only after all classes loaded by this loader have no live instances anymore, and there are no references to these classes and its class loader, and a GC did run (see: JLS 12.7. Unloading of Classes and Interfaces).

Metadata lifecycle - Deallocation

Memory is often retained!

However, “releasing Metaspace” does not necessarily mean that memory is returned to the OS.

All or a part of that memory may be retained within the JVM; it may be reused for future class loading, but at the moment it remains unused within the JVM process.

How large that part is depends mainly on how fragmented Metaspace has gotten - how tightly interleaved used and free portions of the Metaspace are. Also, one part of Metaspace (Compressed Class Space) will not be returned to the OS at all.

Note: An in-depth look at the release logic follows in Part 2.

Note: Concern about Metaspace footprint is a good reason to upgrade to JDK 11 - it got a number of significant improvements in this area, reducing fragmentation and memory waste.

MaxMetaspaceSize, CompressedClassSpaceSize

There are two parameters to limit Metaspace size:

  • -XX:MaxMetaspaceSize determines the maximum committed size the Metaspace is allowed to grow. It is by default unlimited.
  • -XX:CompressedClassSpaceSize determines the virtual size of one important portion of the Metaspace, the Compressed Class Space. Its default value is 1G (note: reserved space, not committed).

We will talk about this in more detail, see Sizing Metaspace.

Metaspace and GC

Metaspace is only released when a GC did run and unload class loaders. Therefore at some point it makes sense to run a GC to clean up stale class metadata, even when there is not much gained by a GC in the regular Java Heap. A Metaspace-induced GC is triggered in two cases:

  • When allocating Metaspace: the VM holds a threshold beyond which it will not grow the Metaspace without attempting to collect old class loaders first - and thereby reusing their Metaspace - by triggering a GC. That point is the Metaspace GC threshold. It prevents Metaspace from growing without releasing stale metadata. That threshold moves up and down, roughly following the sum of committed metaspace by a certain margin.

  • When encountering a Metaspace OOM - which happen either when the sum of committed memory hits the MaxMetaspaceSize cap or when we run out of Compressed Class Space - a GC is attempted to remedy this situation. If it actually unloads class loaders this ins fine, if not we may run into a bad GC cycle even though we have enough Java heap.


  1. Which is a simplification. Metaspace is owned by a ClassLoaderData object, which usually, but not always, has a 1:1 relationship with a class loader. For details, see here [return]