Project Valhalla Arrives: Value Classes Ship in JDK 28 After a Decade of Work

TL;DR
Java value classes (JEP 401) arrive in JDK 28 as a preview, removing object identity overhead for dense memory layouts. What changes in your code.
Last updated: June 19, 2026
Project Valhalla has been the "coming soon" feature of Java for over a decade. It officially launched in 2014, went through five prototype iterations, and accumulated a mythology of "it will never ship" predictions. Now it's shipping. JDK 28 includes JEP 401: Value Classes and Objects as a preview feature, representing over 197,000 lines of changes across 1,816 files.
The summary: you can now write classes that "code like a class but work like an int." Dense memory layouts, no identity overhead, better cache locality. The performance implications are significant for data-heavy applications.
The Problem Valhalla Solves#
Java's type system has a fundamental split: eight primitive types (int, long, double, etc.) and reference types (everything else). Primitives live on the stack and can be packed densely in arrays. Objects live on the heap, require pointer indirection, carry metadata overhead, and need garbage collection.
This worked fine when CPUs and memory were roughly matched in speed. Modern hardware has changed the equation - CPUs are orders of magnitude faster than main memory access. Data locality matters. A lot. DuckDB's internals are a good example of how much performance is left on the table when a system respects memory layout instead of scattering data across the heap.
Consider a Point class with two integers:
// Before Valhalla
class Point { int x, y; }
Point[] points = new Point[1_000_000];
// Result: 1 million pointers to scattered heap objects
Each access requires dereferencing, risking cache misses. Each object carries per-object metadata. The array stores pointers, not the data itself.
How Value Classes Work#
JDK 28 introduces the value modifier:
// After Valhalla
value class Point { int x, y; }
Point[] points = new Point[1_000_000];
// Result: dense, contiguous 8-byte pairs
The key characteristic: no identity. Two value objects with identical content are considered equal, like integers. This has major consequences:
==checks field equality (substitutability), not reference identitysynchronizedthrowsIdentityException- you can't lock on a value object- Helper methods
Objects.requireIdentity()andObjects.hasIdentity()let code handle identity-dependent patterns
Memory Optimization Mechanisms#
Two mechanisms enable the performance wins:
Scalarization. The JIT compiler decomposes value objects into their constituent fields, eliminating allocation overhead entirely. A Color with three bytes gets passed directly as three values, not as a pointer.
Heap Flattening. Value objects can be stored inline within fields or arrays, creating dense sequential data structures. Instead of an array of pointers to scattered objects, you get contiguous packed data.
There's a constraint: flattened data must be readable/writable atomically, typically limited to 64 bits on current platforms. Larger value classes may fail to flatten and revert to heap storage.
What's Included (and What's Not)#
Included in JDK 28 (Preview):
valueclass/record syntax- Scalarization and heap flattening for qualifying types
- Cheaper boxing through value-based wrappers (Integer, Long, Double become value classes)
Not included:
- Null-restricted types (deferred to a separate JEP)
- Full specialized generics
- 128-bit encodings
The null-restriction piece surprises people. Value classes remain reference types in JDK 28, so Point p = null; is legal. Making them non-nullable is a separate language change coming later.
What HN Is Saying#
The Hacker News discussion (210 points, 88 comments) is characteristically technical:
The .NET comparison. Multiple commenters pointed out that C# had structs from the beginning. The debate is whether Java took twelve years to "copy" .NET or whether the backward-compatibility constraints make this genuinely different work. The Java team's position: maintaining compatibility with decades of existing code while adding these semantics is the hard part, not implementing value types in isolation.
On the complexity claims. Some skepticism about the "mentally heavy" justification for dropping dual projections (value/reference variants). Kotlin and TypeScript developers pointed out that nullable vs non-nullable distinctions don't seem to confuse their users.
The generics gap. Several commenters noted that without specialized generics, a List<Point> still materializes value objects as heap objects, negating flattening benefits. This is acknowledged as Phase 2 work - not in JDK 28.
Second-mover advantage. One thread traced C#'s cleaner design to being built later by engineers who had worked on Java's pain points firsthand. Anders Hejlsberg specifically designed around flaws he'd encountered.
The Boxing Revolution#
A less-discussed but practically significant change: primitive wrappers themselves become value classes when preview features are enabled. Since Integer, Long, and Double lose identity, the JVM can scalarize and flatten them.
This means Integer[] approaches int[] efficiency. Boxing overhead - one of Java's perennial performance complaints - gets dramatically reduced.
Timeline and Adoption#
JDK 28 releases in March 2027, with integration targeted for July 2026. The feature ships as a preview, meaning you need --enable-preview to use it and the syntax may evolve before finalization.
For most enterprises, the practical encounter with mature Valhalla will be through the next LTS release (JDK 29, September 2027). Early experimentation now shapes the feedback that determines refinements.
Migration is straightforward: add the value modifier where identity isn't needed. Most code remains binary-compatible, though new errors emerge for identity-dependent patterns like synchronization or reference comparison.
Why This Matters for Performance-Sensitive Code#
If you're working on:
- Numerical computing
- Game engines
- Financial systems with large data structures
- Anything iterating over millions of small objects
Value classes offer meaningful performance improvements without changing your programming model. The class still encapsulates behavior. You still get methods, validation in constructors, and type safety. But the runtime can now treat your data like primitives when beneficial. It is the same tradeoff at play in infrastructure debates like memcached versus Redis - simpler, denser representations often outperform flexible ones once you are moving enough data.
The caveat: this is foundation work. The full vision - specialized generics that let ArrayList<Point> truly flatten - is still coming. But the pieces are landing, and after twelve years, that's worth noting.
FAQ#
What are value classes in Java?#
Value classes, introduced by JEP 401 as a preview in JDK 28, are classes declared with the value modifier that have no object identity. Two value objects with identical content are considered equal, like integers, which lets the JVM scalarize them and flatten them into dense memory layouts.
How do value classes change == and synchronized?#
With no identity, == checks field equality (substitutability) instead of reference identity, and synchronized on a value object throws IdentityException. The helper methods Objects.requireIdentity() and Objects.hasIdentity() let code handle identity-dependent patterns.
Can a value class be null in JDK 28?#
Yes. Value classes remain reference types in JDK 28, so Point p = null; is legal. Null-restricted types are deferred to a separate JEP.
Do I need a flag to use value classes?#
Yes. The feature ships as a preview, so you need --enable-preview to use it, and the syntax may evolve before finalization.
Continue Reading#
- A Free Compilers Textbook That Actually Teaches You to Build One
- GitHub Copilot SDK for Java: Annotations, Virtual Threads, and BYOK for Enterprise Agent Harnesses
- OpenJDK Bans AI-Generated Code: What the New Policy Means for Java Contributors
- Scarf Drops Haskell After 7 Years - LLMs Changed the Calculus
Sources#
- Project Valhalla, Explained: How a Decade of Work Arrives in JDK 28 - Original deep dive
- Hacker News Discussion - 210 points, 88 comments
- JEP 401: Value Classes and Objects (Preview) - OpenJDK specification
- JEP 8303099: Null-Restricted Value Class Types - Future null-restriction work
Get the next deep dive like this in your inbox
One email a week on News and the rest of the AI dev stack. Free.
Read next
Scarf Drops Haskell After 7 Years - LLMs Changed the Calculus
Scarf moves from Haskell to Python after 7 years: why LLM-driven development made compile times a bottleneck, how the migration worked, and the HN debate.
7 min readRoc's Rust-to-Zig Rewrite: 487 Days, 300K Lines, and What the Numbers Actually Show
Roc's Rust-to-Zig compiler rewrite took 487 days. What the memory safety numbers, 35ms incremental rebuilds and the HN reaction actually show.
7 min readA Free Compilers Textbook That Actually Teaches You to Build One
Douglas Thain's free compilers textbook walks you through building a C-like compiler that emits real X86 and ARM assembly, plus what HN recommends reading next.
6 min readNew here? Start with
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.





