Which HotSpot flags bound class-metadata memory, and why does a JVM report a separate 'compressed class space' in addition to overall metaspace usage?
answer
- Class space = contiguous reservation for type structures
- Compressed class pointer needs a bounded address range
- CompressedClassSpaceSize default 1 GB, reserved not committed
- MaxMetaspaceSize caps the total, unlimited by default
- Two distinct OOM messages, two distinct flags
basics
~20 s-XX:MaxMetaspaceSize caps all class metadata. When compressed class pointers are enabled, type structures go into a separate contiguous native reservation, the compressed class space, sized by -XX:CompressedClassSpaceSize (1 GB reserved by default). Exhausting either raises an error.
solid answer
~50 sMetaspace is split into two native areas. The **class space** holds the fixed-size type structures whose addresses must be encodable in a compressed 32-bit class pointer stored in every object header, so it must be one **contiguous reservation** with a bounded address range; `-XX:CompressedClassSpaceSize` sets that reservation, default 1 GB. The **non-class space** holds everything else (method metadata, bytecode, constant pools, dispatch tables) and needs no address-range constraint, so it is committed in chunks wherever the OS provides them. `-XX:MaxMetaspaceSize` bounds the total; by default there is no limit. `-XX:MetaspaceSize` is the initial high-water mark that triggers class unloading, not a floor. Two distinct failures follow: exhausting the total gives `OutOfMemoryError: Metaspace`; exhausting the contiguous class-space reservation gives `OutOfMemoryError: Compressed class space`, and raising `MaxMetaspaceSize` alone will not fix it. Note that reserved address space is not committed memory, so the 1 GB default costs virtual address range, not resident pages.
code
text · 6 lines-XX:MaxMetaspaceSize=256m # caps class + non-class metadata (default: unlimited)
-XX:MetaspaceSize=128m # initial high-water mark that triggers class unloading
-XX:CompressedClassSpaceSize=512m # contiguous reservation for type structures (default 1g)
# Read the steady-state footprint before choosing numbers:
# jcmd <pid> VM.metaspace summarygo deeper
Know the two names: MaxMetaspaceSize caps metadata overall, and there is a separate compressed class space for type structures.
Explain why the class space is contiguous (compressed class pointers in object headers) and that its size is fixed at startup by its own flag.
Distinguish the two out-of-memory outcomes and their different fixes, separate reserved from committed, and size the cap empirically from steady-state usage after warm-up.
Argue caps as a containment policy: bound every native region so a defect surfaces as an attributable JVM error rather than an unexplained container kill, while treating the cap as containment rather than a remedy.
## Two areas, one budget Since JDK 8 HotSpot divides class metadata into **class space** and **non-class space**. The split exists purely because of pointer compression. Every Java object header carries a pointer to its type structure. On 64-bit machines HotSpot normally stores that as a **compressed class pointer**: a 32-bit value that is decoded by scaling and adding a fixed base. That encoding only works if every type structure lives inside one contiguous region of bounded size, so HotSpot **reserves** a single virtual range for them up front. `-XX:CompressedClassSpaceSize` sizes that reservation; the default is 1 GB. Everything else (method metadata and bytecode, runtime constant pools, dispatch tables, annotations) is referenced by ordinary 64-bit pointers and is committed piecemeal into the non-class space. The distinction matters when reading memory-pool metrics: a JVM exposes "Metaspace" (the whole thing) and "Compressed Class Space" (just the type structures) as separate pools, and the second is a subset, not an addition. ## The flags that matter - **`-XX:MaxMetaspaceSize`** caps committed metadata memory overall, class plus non-class. **Unlimited by default.** - **`-XX:MetaspaceSize`** is the initial **high-water mark**: when committed metaspace first crosses it, the JVM runs a collection to attempt class unloading, then moves the mark up or down according to how much was reclaimed, within `MinMetaspaceFreeRatio`/`MaxMetaspaceFreeRatio`. It is neither a floor nor a ceiling. - **`-XX:CompressedClassSpaceSize`** sets the size of the contiguous class-space **reservation**. It is fixed at startup and cannot grow. - **`-XX:-UseCompressedClassPointers`** turns the split off entirely: type structures then use full-width pointers, no class space is reserved, object headers grow, and the flag above becomes irrelevant. It is rarely the right answer. Compressed class pointers are enabled by default on 64-bit HotSpot; a very large heap that disables compressed object pointers historically disabled them too, though the two settings are independent in modern releases. ## Reserved versus committed A frequent misreading: the 1 GB default class space alarms people looking at virtual-memory figures. **Reserving** address space maps nothing; pages are committed only as classes load. On a 64-bit process, address space is nearly free. The number to watch is *committed*, which is what memory-pool metrics and `jcmd <pid> VM.metaspace` report next to the reservation. ## Two failure modes, two fixes Because the class space is a fixed reservation while the non-class space grows against the global cap, exhaustion can come from either side. - Total committed metadata hits `MaxMetaspaceSize` after class unloading fails to help: the JVM raises `OutOfMemoryError: Metaspace`. Fix is a larger cap, or fewer/shorter-lived class loaders. - Loading an enormous number of *distinct types* fills the contiguous class-space reservation while the total is still under the cap: the JVM raises `OutOfMemoryError: Compressed class space`. Raising `MaxMetaspaceSize` does nothing; you must raise `-XX:CompressedClassSpaceSize` at startup, or accept full-width class pointers. Being able to name the second case, and to say that its fix is a *different* flag, is what separates a memorised answer from an operational one. ## Sizing in practice Steady-state metadata footprint is a property of the deployment: roughly, how many classes each loader loads, times the metadata per class, times the number of live loaders. Frameworks that generate proxies, bytecode-weaving agents, scripting engines and repeated hot deployment all multiply the class count. The reliable method is empirical: run a representative workload past warm-up, read committed metaspace from `jcmd <pid> VM.metaspace summary`, then set `MaxMetaspaceSize` with headroom above that plateau. Why set a cap at all when the default is unlimited? Containment. In a memory-limited container an unbounded region turns a class-loading defect into an out-of-memory kill of the whole process, with no attribution. A cap converts the same defect into a specific, attributable Java error thrown by the JVM while it is still alive enough to log it, dump diagnostics and exit cleanly. One caveat worth stating: caps prevent the process from silently eating the container, but they do not prevent the underlying growth. A cap is a containment device, never a fix for a class-loading defect.
- An application fails with OutOfMemoryError: Compressed class space while committed metaspace is far below MaxMetaspaceSize. What do you change?Raise `-XX:CompressedClassSpaceSize` at startup, since the class space is a fixed contiguous reservation that cannot grow and is unaffected by the global metaspace cap. The alternative is disabling compressed class pointers, which removes the reservation but widens object headers and costs footprint everywhere. Either way, first ask why the process holds so many distinct types alive.
- Does the 1 GB default class space consume 1 GB of physical memory?No. It is a virtual-address reservation; pages are committed only as type structures are allocated. Resident memory tracks committed metaspace, not the reservation, which is why the figure looks alarming in virtual-size columns and harmless in resident ones.
saying these in an interview costs you the question
- Treating compressed class space as memory added on top of metaspace rather than a subset of it
- Believing MaxMetaspaceSize raises the class-space limit
- Confusing reserved address space with committed physical memory
- Calling -XX:MetaspaceSize a guaranteed minimum allocation
- Assuming the default configuration caps metadata at some safe built-in value