Files
javademos/docs/03-compact-headers.md

2.4 KiB

3. Compact object headers are the default (JEP 534)

Prev: 2. G1 · Next: 4. Post-quantum TLS

Every Java object starts with a header. On JDK 26 with default settings it is 12 bytes (8 bytes mark word plus a 4-byte compressed class pointer). With compact object headers the two are squeezed into 8 bytes. JEP 534 makes that the default in 27; -XX:-UseCompactObjectHeaders turns it off.

Measured with JOL

scripts/object-headers.sh downloads JOL 0.17 (sha1-checked) and runs HeaderDemo three ways. The summary is 15; raw runs are 10 (26), 11 (27), 12 (27 with the opt-out, which reproduces 26 exactly).

Object JDK 26 JDK 27
Object 16 B 8 B
Point { int x; int y; } 24 B 16 B
Order { long id; int quantity; boolean paid; String customer; } 32 B 32 B
boxed Long 24 B 16 B
boxed Integer 16 B 16 B
int[3] 32 B 24 B
1,000,000 boxed Long in an ArrayList 28.9 MB 20.9 MB
1,000,000 Point in an ArrayList 28.9 MB 20.9 MB
1,000,000 boxed Integer in an ArrayList 20.9 MB 20.9 MB

The pattern to notice: saving 4 bytes only helps when it crosses an 8-byte alignment boundary. Order and boxed Integer do not move, because padding was already absorbing the difference. Your heap will not shrink by "4 bytes times object count"; it shrinks by the number of objects that were sitting just past a boundary.

Two things that bite

  • JOL 0.17 cannot inspect a record. Unsafe.objectFieldOffset refuses records (13). The workaround -Djol.magicFieldOffset=true prints a layout but shows the header mark as N/A (14).
  • Anything that hard-codes 12 bytes (off-heap size estimators, "object overhead" constants in capacity plans, tests that assert Instance size) is now wrong by default. -XX:+UseCompressedClassPointers is also no longer an option in 27: the JVM prints "Ignoring option UseCompressedClassPointers; support was removed in 27.0" (60).

Prev: 2. G1 · Next: 4. Post-quantum TLS