Project Valhalla in JDK 28: What Value Classes Actually Change
JEP 401 just landed in OpenJDK mainline. Here is how to try value classes in JDK 28, what scalarization actually does, and the null surprise nobody expects.
On this page
Project Valhalla has been Java's most anticipated, slowest arriving feature for over a decade. On June 15, 2026, Oracle engineer Lois Foltan confirmed the first concrete piece, JEP 401 (Value Classes and Objects), is integrating into OpenJDK's mainline this month, targeting a preview release in JDK 28. The pull request is 197,000 lines across 1,816 files. Foltan asked other OpenJDK committers to hold off on large commits during the integration window because of the scale involved.
If you have heard "Valhalla" mentioned for years without anything shipping, this is the moment something concrete actually lands. Here is what it changes, what it does not change yet, and what you can try right now.
What Actually Got Confirmed This Week
JEP 401 (Value Classes and Objects) is the first deliverable from Project Valhalla after 12 years of development. The integration into OpenJDK mainline targets a preview in JDK 28.
The scale is significant: 197,000 lines of code across 1,816 files. Oracle described it as the biggest change to Java's object model since the language's creation in 1995. Foltan explicitly asked other OpenJDK committers to pause large commits during the integration window to avoid merge conflicts at that scale.
The Problem Valhalla Solves, in Plain Terms
In Java, everything except eight primitive types (int, long, double, boolean, and the rest) is a reference type. When you write:
Point p = new Point(1, 2);p is not a point. It is a pointer, a reference to an object sitting somewhere on the heap. Every time you read a field on p, the JVM has to follow that pointer first. For a single object, this overhead is invisible. For a million-element array of Point objects, you are not getting a contiguous block of coordinate data. You are getting a million pointers scattered across the heap, each requiring a separate dereference.
This is why performance-critical Java code has historically resorted to "primitive obsession" (using raw int[] arrays instead of a clean Point[] array), trading readability and type safety for the memory layout the hardware actually wants. Valhalla's stated goal, in Brian Goetz's words, is to let code "look like a class, work like an int."
How to Try It This Week
Early access builds are available now from jdk.java.net/valhalla. To enable the preview features in your own code:
javac --enable-preview --release 28 Point.java
java --enable-preview PointA minimal value class:
value class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}And the same thing as a value record, usually the better choice when your type is genuinely just a tuple of data:
value record Point(int x, int y) {}Value Class vs Record: What Is Actually Different
Both give you generated equals(), hashCode(), and toString(). Both are immutable by convention. The real distinction is about identity.
A value class gives up object identity entirely. Instances are compared by their component values, not by reference. Calling synchronized on a value class instance now throws an exception, because synchronization fundamentally requires identity.
In exchange for giving up identity, you get the JVM's freedom to optimize. A reference type has to support == reference comparison and locking, which means the JVM cannot safely flatten it into a register or an array slot. It has to maintain that pointer. A value class makes no such promise, so the JVM is free to scalarize or flatten it.
| Value Class | Record | |
|---|---|---|
| Generated equals/hashCode/toString | Yes | Yes |
| Immutable fields | Yes (implicit) | Yes (implicit) |
| Object identity | No (given up) | Yes (retained) |
| synchronized allowed | No (throws exception) | Yes |
| JVM can scalarize/flatten | Yes | No |
| Can be a value record | Yes (value record) | Yes (value record) |
The Detail That Will Surprise You
Here is the part everyone gets wrong on first read: value classes can still be null.
Point p = null; // perfectly legal in JDK 28's value class model"Value type" intuitively sounds like "works like a primitive, so no null," but that is a separate, later JEP. Non null types are not part of JDK 28. In this preview, value classes are still reference types under the hood; they have just given up identity, not nullability.
Do not write code assuming a value class field can never be null until that separate JEP lands.
| Type category | Identity | Nullable | JDK 28 status |
|---|---|---|---|
| Primitives (int, long, etc.) | No | No | Unchanged |
| Value classes (new) | No | Yes | Preview in JDK 28 |
| Regular classes (Object, etc.) | Yes | Yes | Unchanged |
| Non null value types (future) | No | No | Not in JDK 28 |
What Scalarization and Heap Flattening Actually Mean
Take a Color value class with three byte fields for red, green, and blue. Without Valhalla, passing a Color to a method means passing a pointer to a heap-allocated object: an allocation, a pointer dereference, and GC pressure for an object that is conceptually just three bytes.
With scalarization, when the JIT compiler can prove the concrete type at a call site, it breaks the value object down into its constituent fields at the machine code level. Instead of passing a pointer to a Color, it passes three bytes plus a null flag directly. No allocation happened. There is nothing for the garbage collector to track.
Heap flattening handles the array case: instead of an array of pointers to separately allocated Color objects scattered across the heap, the array stores the actual byte data inline, contiguously. That is the layout you would get from a hand-rolled byte[] encoding, but with a real class, real field names, real validation in the constructor, and real methods. For applications that rely on caching strategies for performance, eliminating object overhead at the memory level is a complementary optimization.
Migrating Existing JDK Classes
java.lang.Integer and other primitive wrapper classes are migrating to value classes under this preview. This is where Goetz's warning about breaking changes becomes concrete: code that calls synchronized on Integer objects now fails with an exception.
If you have code anywhere that does synchronized(someInteger) { ... }, a pattern most developers do not realize they rely on until it breaks, this preview will surface it immediately. Search your codebase for synchronized blocks on wrapper types before enabling the preview.
What Is Explicitly Not in JDK 28
Goetz has been direct about scope: "this is just the first part of Valhalla." The following features are not included in JDK 28:
- Null restricted (non nullable) types are a separate JEP, not part of this preview
- Full specialized generics (
List<int>) are not available yet - 128 bit value encodings are not supported
- Vector API remains in incubation (it depends on Valhalla's underlying VM primitives landing first)
JDK 28 itself is not a Long Term Support release. That is expected to be JDK 29 in September 2027, and Goetz has cautioned that hoping for Valhalla to fully exit preview by then is "optimistic."
Should You Use This Now?
It is a preview feature, disabled by default, and the syntax can still change release to release based on feedback, which is the entire point of shipping it as a preview rather than a finished feature.
If you work on performance-critical Java (data processing, numerics, game engines, anything currently fighting object overhead with hand-rolled primitive encodings), this is worth experimenting with now and reporting real-world findings to the valhalla-dev mailing list. It is not yet something to migrate production code onto. The team explicitly wants feedback from real applications before the design settles further. If you are planning a migration, the experience is similar to how teams approached the TypeScript 7 migration: experiment in a branch, benchmark, and wait for stability before committing production code.
Frequently Asked Questions
What is Project Valhalla?
Project Valhalla is a long-running OpenJDK initiative to redesign Java's type system so that user-defined types can behave like primitives at the JVM level. Its goal is to eliminate the performance overhead of heap allocation and pointer indirection for small, immutable data types, while keeping the expressiveness of classes. The phrase Brian Goetz uses is "codes like a class, works like an int."
The first concrete deliverable is JEP 401 (Value Classes and Objects), which entered preview in JDK 28 in June 2026 after more than 12 years of design work.
Can value classes be null in Java?
Yes. In the JDK 28 preview, value classes are still reference types. They have given up object identity (no reliable == reference comparison, no synchronized) but they remain nullable. Writing Point p = null; is perfectly legal.
Non nullable (null restricted) types are planned as a separate, later JEP and are not part of JDK 28.
What is the difference between a value class and a record in Java?
Both are immutable and generate equals(), hashCode(), and toString(). The key difference is identity: a record retains object identity (you can synchronize on it, == tests reference equality), while a value class surrenders identity entirely. Giving up identity is what lets the JVM scalarize and flatten value classes for better performance.
You can combine both: a value record is a record that also gives up identity. For pure data carriers, value record Point(int x, int y) {} is usually the most concise option.
How do I enable Valhalla preview features in JDK 28?
Download an early access build from jdk.java.net/valhalla, then compile and run with the --enable-preview flag:
javac --enable-preview --release 28 YourFile.java
java --enable-preview YourFileFor Maven or Gradle projects, add the --enable-preview flag to both the compiler and the JVM arguments in your build configuration.
When will Project Valhalla be stable and out of preview?
There is no confirmed timeline. JDK 28 ships the first preview of JEP 401 (value classes). The next Long Term Support release is expected to be JDK 29 in September 2027, but Brian Goetz has described hoping for Valhalla to fully exit preview by then as "optimistic."
The broader Valhalla roadmap includes additional JEPs (null restricted types, specialized generics) that have not yet entered preview. Full completion will span multiple JDK releases.
How much faster are value classes compared to regular Java objects?
The performance benefit depends on your workload and how the JIT compiler optimizes your specific code. The two main optimizations are scalarization (breaking a value object into its component fields, eliminating heap allocation entirely) and heap flattening (storing values inline in arrays instead of as pointers to separate objects).
For data-heavy applications that process large arrays of small objects (coordinates, colors, timestamps), the improvement can be substantial because you eliminate millions of pointer dereferences and reduce GC pressure. For code that mostly works with single instances, the difference will be minimal.
Related Articles
GitHub Actions Tutorial: CI/CD from Push to Deploy (2026)
Learn GitHub Actions: write your first workflow, run tests automatically, use secrets safely, deploy via SSH, cache dependencies, and run matrix builds.
GitHub Actions Security: 7 Misconfigurations to Avoid
The 7 GitHub Actions misconfigurations behind real supply chain attacks: weak GITHUB_TOKEN scope, pull_request_target, unpinned actions, script injection.