↓ Skip to main content

Compact Object Headers by Default: JEP 534 in Java 27

·4 mins

Every single object you allocate on the JVM heap carries a header before any of its actual fields — identity hash code, GC bookkeeping, lock state, a pointer to its class. That header has been 96 bits since compressed oops became the norm. JEP 534 shrinks it to 64 bits, and as of JDK 27, that shrinkage is on by default instead of something you had to know to ask for.

Why the header size matters at all
#

96 bits doesn’t sound like much until you remember it’s paid by every object, not just big ones. A Long wrapper, a linked-list node, an Optional — for small objects the header can be a third or more of the object’s total footprint. Heap-dense workloads (lots of small, short-lived objects — which describes a large share of typical Java services) pay that tax proportionally harder than anything else. Smaller headers mean more objects fit in cache lines and in the heap itself, which is where the “increasing data locality” part of the JEP’s stated goal actually comes from — it’s not just about bytes saved, it’s about how much more of the heap fits where the CPU can reach it quickly.

History
#

Below is the history of the feature

JDK JEP Status
24 JEP 450 Introduced as an experimental, opt-in feature
25 JEP 519 Promoted to a full product feature — still opt-in
27 JEP 534 Default on — the old 96-bit layout is still available, just no longer the default

Straight from the JEP’s own motivation section:

“Since JDK 24, compact object headers have proven their stability and performance. They have been tested at Oracle by running the full JDK test suite. They have also been tested at Amazon and SAP. Amazon runs hundreds of services in production with compact object headers, most of them using backports of the feature to JDK 21 and JDK 17. SAP has already switched to compact object headers by default in their downstream OpenJDK fork, the SapMachine.”

That’s a meaningfully different claim than “we benchmarked this in a lab.” Two large, separate organizations ran this in production, on their own backports, before the JDK project itself turned it on by default.

What it actually buys you
#

Numbers from the JEP, unmodified:

  • SPECjbb2015 uses 22% less heap space and 8% less CPU time
  • Garbage collections during that same SPECjbb2015 run drop by 15% (holds for both G1 and Parallel)
  • A highly parallel JSON parser benchmark runs 10% faster

None of that requires touching application code. It’s a JVM flag default flip, not an API.

Flipping it off, if you need to
#

The flag itself hasn’t changed — only its default value has, which means the meaning of not passing it flipped between JDK 25 and JDK 27:

# JDK 24-25: had to opt in explicitly
java -XX:+UseCompactObjectHeaders -jar app.jar

# JDK 27+: on by default; opt out explicitly if you need to
java -XX:-UseCompactObjectHeaders -jar app.jar
If you have a deployment script or a Dockerfile carrying -XX:+UseCompactObjectHeaders forward from a JDK 24/25 upgrade, it’s harmless on JDK 27 — you’re just being explicit about a value that’s now already the default. But if you have monitoring or tooling that reads raw heap dumps or does anything with object layout assumptions, that’s the thing actually worth testing before this upgrade, not the flag itself.

What happens if you need the space back later
#

The JEP reserves four bits of the new 64-bit header up front for Project Valhalla (value types), which is a reasonable thing to want to know before you go build something that assumes every remaining bit is free forever. Its own risk section is short and direct about the headroom that’s left:

“If a need for even more bits arises, we can shrink compressed class pointers and identity hash codes even further, using techniques we have already prototyped in Project Lilliput.”

And notably: removing the old 96-bit layout entirely is explicitly not a goal of this JEP. If compact headers ever cause a problem for you specifically, the escape hatch stays available — it’s just not the path most people will take anymore.

Related