Representative interview topic

Coding interview: How would you evaluate and roll out Java 25 compact object headers?

CodingHard
Offer.cc Editorial TeamPublished Updated

Question

Your team is upgrading to Java 25 and wants compact object headers to reduce heap use for millions of small objects. Explain what changes, why it is not enabled by default, and how you would verify gains and compatibility.

Prompt and context

Your team is upgrading to Java 25 and wants compact object headers to reduce heap use for millions of small objects. Explain what changes, why it is not enabled by default, and how you would verify gains and compatibility.

JEP 519 promotes the experimental compact object headers from Java 24 to a product feature in Java 25. On 64-bit architectures the header can be compacted to 64 bits, but it still requires explicit opt-in; the interview tests HotSpot layout, runtime flags, and measurement boundaries.

What the interviewer is testing

Cover the information in an object header, compressed-class-pointer and layout constraints, coexistence of locks and identity hashes, the enablement flag and default, causal heap and GC metrics, and a rollback and version-compatibility plan.

30-second answer framework

“I would treat compact headers as an optional HotSpot memory layout. JEP 519 reduces common 96- or 128-bit headers to 64 bits, lowering fixed per-object overhead and potentially improving heap density and locality, but Java 25 still leaves it disabled by default. I would pin the JDK, verify compressed-class-pointer conditions, run a production-shaped allocation benchmark comparing RSS, live set, GC, and throughput, and keep -XX:-UseCompactObjectHeaders as a rollback.”

Step-by-step deep dive

Step 1: State what the header stores

HotSpot headers encode class identity, object marks, lock state, identity hash codes, and GC-related information. The compact layout repacks those values into a 64-bit word; it changes VM representation, not Java fields or language semantics.

Step 2: Explain the size benefit

With conventional 64-bit settings, headers are commonly 96 bits and can be 128 bits when compressed class pointers are disabled. Compact headers target 64 bits, saving at most 4 or 8 bytes per object; total benefit depends on object count and object size, so the saving cannot be applied directly to the whole heap.

Step 3: Describe enablement and the flag

Java 25 makes the option a product flag. Enable it explicitly with:

bash
java -XX:+UseCompactObjectHeaders -jar service.jar

The feature is still not the default layout. Startup scripts, container images, and diagnostics must record the complete JVM command line so nodes do not silently use different layouts.

Step 4: Handle class-pointer and class-count limits

The compact layout relies on the encoding space of compressed class pointers. Class-loading scale, generated proxies, and module count can affect suitability; a tiny local program is not enough. Validate startup with a production-like class path, proxy generation, and flags.

Step 5: Analyze locks and identity hashes

Header bits participate in both lock state and identity-hash management. Include synchronized, wait/notify, and System.identityHashCode in regression tests. Application code need not change, but the VM may allocate monitor structures after contention or hash computation.

Step 6: Build a causal benchmark

Create an object graph with the production lifecycle, run enabled and disabled groups, warm up, and repeat. Record heap peak, live set, allocation rate, GC pauses, throughput, startup time, and container RSS with confidence intervals; one -Xmx value or one latency percentile cannot prove the benefit.

Step 7: Verify tools and compatibility

Use the target JDK's GC logs, JFR, and jcmd VM.flags to confirm the actual flag. Check diagnostic tools, JVMTI agents, crash dumps, and monitoring parsers. Re-run startup, lock-contention, serialization, and heap-dump checks before minor JDK upgrades.

Step 8: Define a canary and rollback

Canary a small fraction on identical hardware and load, with heap density and pause time as gates. If startup fails, RSS does not fall, or lock contention regresses, switch to -XX:-UseCompactObjectHeaders. Keep both command lines and comparable benchmark data; do not mix layout changes with other JDK changes in one release.

Trade-offs and boundaries

Memory density versus diagnostic complexity

Small-object-heavy services may gain density, while header encoding and tool support become more complex. If objects are few and large, profile header share before accepting upgrade risk.

Throughput versus pause time

Smaller objects do not guarantee every GC metric improves. Lower heap use can reduce scanning, yet lock or hash paths can change tail latency; evaluate throughput, pauses, and the error budget together.

Product flag versus default policy

A product flag in JDK 25 does not mean the default is enabled. Put the value in the runtime baseline and document it in images, launchers, and incident runbooks.

Failure drills and evolution plan

Generated proxies prevent startup

Generate proxies in the full service with the flag enabled and capture VM errors. If class-pointer space is insufficient, roll back and keep other release variables fixed.

Identity-hash path regresses

Call System.identityHashCode on many objects while running lock contention and wait/notify, then compare pauses, throughput, and monitor counts.

Observations use different windows

If JFR, GC logs, and container metrics cover different windows, align sampling and warm-up first; do not compare maxima from different loads.

Common mistakes and follow-ups

Mistake 1: Assuming every object saves eight bytes

Follow-up: Why not multiply object count by eight? Header size depends on the old layout and compressed class pointers; alignment, arrays, TLAB fragmentation, and other heap structures affect the total.

Mistake 2: Treating a product flag as the default

Follow-up: Is Java 25 enabled by default? No. Use UseCompactObjectHeaders explicitly and record it in the runtime baseline.

Mistake 3: Proving success with throughput alone

Follow-up: What else matters? At least live set, RSS, GC pauses, lock contention, identity-hash paths, and diagnostic-tool compatibility.

Extended follow-ups and model answers

Which services should be tested first?

Services with huge object counts, small average objects, and heap pressure are the best candidates: high-concurrency caches, event parsing, and short-lived request objects. Few large objects are lower priority.

Why not reuse a Java 24 experiment for Java 25?

Java 25 moves the feature from experimental to a product option, so VM, flags, and tooling can change. Rebuild the benchmark and rollback process on the target JDK.

How do you attribute the gain to compact headers?

Hold hardware, JDK build, GC, heap flags, dataset, and warm-up constant; change only UseCompactObjectHeaders, repeat runs, and compare the same metric set.

Public sources

Related questions

Related interview tool

Use Screenshot for a coding prompt

Capture the problem, then work through the constraints, solution, code, edge cases, and complexity in order.

View the tool