The article frames Valhalla as a re-litigation of the JVM's 1995 foundational decision that every non-primitive is a heap object with identity. This is why it took a decade — it's not analogous to C# structs but a deep change to descriptors, the verifier, and JIT layout logic that had to land coherently across the entire VM.
The article emphasizes that the JIT can now flatten value class instances directly into containing objects, arrays, and stack frames with no header or indirection. A million-element Point array collapses from a million 24-byte heap allocations plus pointer hops into 8MB of contiguous ints — this is described as the entire reason the project exists.
The article is careful to note that JDK 28 ships preview, not GA, and that second-preview value classes still box on null. The flat layout only fires in the common case once null-restricted types (the `Point!` syntax) and the L/Q descriptor split land, so current benchmarks understate what production Valhalla will deliver.
The JVM Weekly explainer that hit 504 on HN this week is, in essence, the closest thing Valhalla has had to a shipping-date announcement. After ten years — the project was greenlit in 2014, with John Rose's first "Value Objects" JEP draft circulating in 2015 — the first user-visible pieces are slated to enter preview in JDK 28 (March 2026). Not GA. Preview. But after a decade of "any year now," that distinction matters less than the fact that the descriptors, the verifier changes, and the JIT layout logic are all in main-line OpenJDK builds today.
The shipping unit is value classes (JEP 401, currently in second preview as of JDK 25). A value class is declared with the `value` modifier, has no identity, cannot be `synchronized` on, and its `` is structural rather than referential. The payoff is that the JIT is now allowed to flatten instances directly into containing objects, arrays, and stack frames — no header, no indirection, no per-instance allocation.== A `value record Point(int x, int y)` inside a `Point[1_000_000]` stops being a million 24-byte heap allocations plus a million pointer hops and becomes 8MB of contiguous ints. That is the entire reason this project exists.
The two pieces still queued behind value classes are null-restricted types (JEP 401's companion, the `Point!` syntax that lets the VM skip the null check) and the L/Q descriptor split at the bytecode level. Both are necessary for the flat layout to actually fire in the common case. The current second-preview value classes still box on null, which means a `Point` field that could be null gets the old layout. The non-null variant is what unlocks the real numbers.
The headline framing — "a decade of work" — undersells the more interesting story, which is what made it take a decade. Valhalla is not a language feature in the C# `struct` sense. It is a re-litigation of the JVM's foundational decision, made in 1995, that every non-primitive has identity. Every `==`, every `synchronized`, every `WeakReference`, every `IdentityHashMap`, every `System.identityHashCode` in the entire ecosystem assumed that. Ripping that assumption out without breaking 30 years of bytecode is the actual engineering problem, and the reason the project burned through three previous designs (value types, inline classes, primitive classes) before landing on the current value-class-plus-null-restriction split.
The community reaction on HN is split along predictable lines. The cohort that benchmarks Java against C++ and Rust is treating this as the most consequential JVM change since invokedynamic; the cohort that ships Spring services is mostly asking "do I have to do anything?" Both are right. For latency-sensitive numerical code — quant libraries, game engines on the JVM, Apache Arrow-style columnar stores — the existing workarounds (parallel arrays, hand-unrolled struct-of-arrays, off-heap with `Unsafe`) become obsolete the moment null-restricted value classes ship for real. For a typical service that spends 80% of its time in I/O wait, the change is invisible.
The sharper question is what happens to the standard library. `Optional`, `LocalDate`, `LocalDateTime`, `Duration`, all the `java.time` types, `Pair`-style records in third-party libs — these are the textbook value-shaped classes, and none of them are value classes yet. Migration is opt-in and source-incompatible in ways that matter: a class that flips to `value` cannot be subclassed, cannot be synchronized on, and breaks any caller that relied on `` reference equality.== The JDK team has signalled they will migrate the obvious candidates, but the exact list and timing aren't pinned. Until `LocalDate` is a value class, the flat-array win in your timeseries code stays theoretical.
On the language-comparison front, the JVM ecosystem now has three different answers to the same problem. Kotlin shipped `value class` (single-field, inline-at-callsite) in 1.5, four years ago, but it's a frontend trick — the underlying JVM still sees the boxed type. Scala 3 has `opaque type` with similar limits. Valhalla is the first answer that pushes the optimization down into the VM itself, which means Kotlin and Scala both get to inherit the flat layout for free once they emit the new descriptors. That is the under-appreciated leverage point: a decade of Oracle's investment quietly upgrades every JVM language at once.
If you maintain a library, the practical move is to audit your candidate types now — anything immutable, equality-by-value, no meaningful subclassing — and start tagging them with the second-preview `value` modifier on a branch. The compiler will tell you immediately which call sites depend on identity. In our experience auditing a mid-size codebase, the surprising blockers are rarely the obvious ones (no one synchronizes on a `Point`). They're things like Jackson polymorphic deserialization, Mockito spies, and the occasional `IdentityHashMap` someone reached for because regular `HashMap` was "too slow." Plan for the audit to take longer than the migration.
If you ship a service, the action item is smaller: pin JDK 28 in a side branch when it drops, run your existing test suite with `--enable-preview`, and watch for any third-party library that has already migrated. The risk surface is libraries that flip a public type to `value` between minor versions — that's a binary-incompatible change dressed as a perf improvement, and the JEP text is explicit that consumers should treat it as a major-version bump even if the library author doesn't. Read the changelogs.
For performance-critical code today, the honest advice is to not rewrite around Valhalla preview yet. Second-preview means the bytecode format can still change, and your `--enable-preview` jar will not run on the next JDK without a recompile. The right time to migrate hot paths is when null-restricted types and the descriptor split land — at which point the JMH numbers stop being academic.
JDK 28 is the inflection point but not the finish line. The realistic GA window for the full feature set — value classes, null-restricted types, and the migrated standard library — is JDK 30 or 31, late 2026 into 2027. That is still the fastest movement Valhalla has shown in its history, and the first time the OpenJDK roadmap has put concrete release numbers next to the pieces. If you've been waiting for the moment to take the project seriously, this is it. If you've been promising your team that flat arrays of structs were coming to Java, you can finally tell them which release to mark on the calendar.
Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.