Java's Value Objects Finally Land in OpenJDK Master
Twelve years into Project Valhalla, a 208,000-line merge puts identity-free objects on track for JDK 28.
Twelve years is a long time to wait for a keyword. This week, Project Valhalla — the OpenJDK effort launched in 2014 to teach the JVM about identity-free data — finally shipped its centerpiece: the implementation of JEP 401: Value Objects was integrated into OpenJDK master via pull request #31120, targeting JDK 28 (March 2027) as a preview feature. The numbers alone tell you why it took this long: 208,000 lines added, 13,000 removed, 1,888 files touched, spanning the language compiler, HotSpot, and the standard library. Oracle asked other committers to pause major work while it landed. Nothing of this scale has hit the Java mainline since the module system.
And unlike the module system, this one changes what your existing code means.
What actually landed
A value class is a class declared with the value modifier whose instances have no identity. Fields and the class itself become implicitly final, and == compares field values instead of memory addresses. Everything else — references, null, methods, generics — works the way it always has. That's the deliberate design center: no new memory model to learn, no struct keyword, no stack-versus-heap annotations. You state a semantic fact ("two instances with equal fields are interchangeable") and the JVM earns the right to flatten your objects into arrays, scalarize them into registers, and skip allocations entirely.
value record Point(int x, int y) {}
new Point(17, 3) == new Point(17, 3) // true
Objects.hasIdentity(new Point(17, 3)) // false
The preview also converts 30 platform classes to value classes, including Integer and the other primitive boxes, Optional, LocalDate, and Duration. The same merge carried JEP 539 (Strict Field Initialization in the JVM), which JEP 401 depends on for safe construction.
The subtle headline is the box conversion. Under --enable-preview on JDK 28, Integer x = 1996, y = 1996; x == y evaluates to true — for thirty years it's been false outside the small-value cache. And synchronized on a LocalDate or an Integer is now a compile error at the exact type, or an IdentityException at runtime through an Object reference. Java has been telegraphing this since JDK 16, when JEP 390 slapped @ValueBased warnings on these classes. The bill for ignoring those warnings comes due here, and it lands on library maintainers first: connection pools, caches, and lock registries that key on boxed values or intern-based sentinels break loudly. Loud is the right call — a runtime exception beats a silently vanished monitor — but if you own a widely-used library, grepping for synchronized on value-based types belongs on this quarter's list, not 2027's.
The bet Valhalla made that C# didn't
C# has had structs since 2002; Kotlin and Scala both bolted on value-class constructs that mostly compile away. Java could have done a struct fifteen years ago. Valhalla explicitly refused, and JEP 401's non-goals section still refuses: no structs, no guaranteed memory layout, no third kind of variable. The wager is that if you fix the semantics — make identity an opt-in property rather than a tax on every allocation — the JVM can apply layout optimizations invisibly and incrementally, the way it did with escape analysis and compressed oops.
That wager cuts both ways, and developers should read it honestly. The JEP shows a LocalDate[] collapsing from a pointer array plus scattered heap objects into a flat array of 64-bit words — int[]-like locality for a proper class. It also concedes that LocalDateTime is too big for that trick, and that flattening is opportunistic, not promised. The dramatic wins — dense flattened arrays of larger values, layouts that drop the null bit — are gated on null-restricted types, a separate JEP still cooking in Valhalla. Brian Goetz has said JEP 401 is "just the first part," and called exiting preview by JDK 29 optimistic. If you're benchmarking JDK 28 expecting your DTO-heavy service to shed half its heap, you'll be disappointed. The realistic near-term payoff is scalarization in hot loops and cheaper small aggregates — real, measurable, not transformative yet.
What to do with this
Try it now rather than in 2027, because the migration work is auditing, not rewriting. Grab a JDK 28 early-access build when they appear (Valhalla EA builds already exist), then:
javac --release 28 --enable-preview Main.java
java --enable-preview Main
Three concrete moves. First, run your test suite with preview enabled and watch for IdentityException and ==-on-boxes assumptions — this costs an afternoon and tells you your real exposure. Second, inventory your record classes: records are already final with final fields, so most are one value keyword away, and immutable-heavy codebases (money types, coordinates, event timestamps) are the first beneficiaries. Third, don't ship value in libraries yet — preview features pin you to a class-file version and force --enable-preview on every consumer, which is fine for apps and poison for libraries.
The strategic read: this is a genuine shift, not hype, but it's a compounding investment rather than a launch-day win. Valhalla's history is littered with abandoned prototypes — Minimal Value Types, Q-types, the L-world experiments — precisely because the team kept rejecting versions that made developers manage memory shapes by hand. What survived is the most conservative possible surface over the most ambitious possible runtime change. Kotlin and Scala get a real target to compile their value types onto instead of erasure tricks. GC-pressure-bound services get relief that arrives release by release without code changes, as more of the platform goes identity-free. And Java finally closes the door on its oldest performance apology — that modeling a date as an object costs you five times the memory of modeling it as an int. It took twelve years and 208,000 lines. On the evidence of this merge, the JVM's semantics-first bet is going to pay out slowly, and then all at once.
Sources & further reading
- 8389219: Implement JEP 401: Value Objects (Preview) — github.com
- JEP 401: Value Objects (Preview) — openjdk.org
- Java's biggest language change in a decade is finally landing — thenextweb.com
Rachel has been embedded in the developer tooling ecosystem for nearly eight years, covering everything from IDE wars and package-manager drama to the quiet rise of AI-assisted coding. She has a soft spot for open-source maintainers and an unhealthy number of terminal emulators installed on a single laptop.
Discussion 0
No comments yet
Be the first to weigh in.