Picture two identical twins. They look the same, they answer to the same questions the same way — but they are still two different people. If you ask "are these the same person?", the honest answer is no, even though they're indistinguishable in every way that matters to you.

That's exactly the trap Java has set for developers since its very first version. And a project called Valhalla — yes, named after the Norse warriors' afterlife, because it has taken that long to get here — is the JDK team's attempt to finally close it.

The bug hiding in plain sight

In Java, almost everything you create — a date, a number wrapped in an object, a custom class — is what's called a reference type. When you create one, Java doesn't just remember the value you gave it. It also stamps it with an invisible identity, like a serial number, and stores it at its own address in memory.

So if you create two dates that both represent June 21, 2026, and ask Java "are these two the same?", using the most natural comparison (==), Java says no. Not because the dates are different — they're identical — but because they're two separate "twins" sitting at two separate addresses.

Think of it like this
Two ₹500 notes are different pieces of paper with different serial numbers — but nobody at a shop cares which exact note you hand over, only that the value is ₹500. Java's reference types behave like a cashier who insists on checking serial numbers before accepting your money. Project Valhalla teaches Java to act like a normal cashier — to care about the value, not the paper.

It gets weirder with plain numbers

Here's the part that trips up even experienced developers. Java's Integer — the object version of a whole number — secretly caches small values. Numbers below 128 get reused from a shared pool, so two small Integer objects with the same value can pass the == check. But the moment the number is 128 or higher, that caching stops, and the same check silently starts failing.

Same code. Same logic. Different answer, purely because of which number you picked. Most Java linters now warn developers never to use == on Integer for exactly this reason — it's a trap that depends on an implementation detail nobody should have to remember.

Today.java — the trap in 4 lines
Integer a = 100; Integer b = 100; a == b // → true (cached, both point to the same object) Integer x = 200; Integer y = 200; x == y // → false (not cached, two different objects)

The fix: a class that gives up its "identity"

JEP 401 — short for JDK Enhancement Proposal 401, the official Valhalla proposal — introduces a new kind of class called a value class. Instances of a value class don't have that invisible serial number at all. Two value objects holding the same data are the same, by definition, because there's nothing left to distinguish them other than their values.

The JDK team plans to gradually migrate existing classes like Integer to this new model, and developers will be able to write their own value classes too.

Reference type vs. value type — what changes in memory
TODAY · REFERENCE
Integer a = 200
↓ points to
Object #4471
value: 200
Integer b = 200
↓ points to
Object #4502
value: 200
a == b → false. Two addresses. Java compares the addresses, not the 200s.
WITH VALHALLA · VALUE
Integer a = 200
↓ stored directly as
200
Integer b = 200
↓ stored directly as
200
a == b → true. No address to compare. Just the value, every time.

Why the JDK team cares about more than fixing a bug

If this were only about fixing a confusing comparison, it might not be worth 12 years of engineering effort. The real prize is performance. Reference types always need an extra hop — go to the variable, follow it to an address, then read the value sitting there. Value types remove that hop entirely, since the value can sit directly where it's used.

For a single number, that hop costs you nothing you'd notice. But software rarely deals with a single number — it deals with millions of them, in loops, in lists, in financial calculations running thousands of times a second. Removing one unnecessary hop, repeated a million times, becomes a measurable speed difference. That's the actual bet Project Valhalla is making.

For the engineers in the room
JEP 401 is the first of several Valhalla layers. It removes object identity, which unlocks new JVM optimizations — particularly for small, frequently-allocated objects — but it does not yet address nullity or atomicity-safety-under-race (ASUR), which Brian Goetz has flagged as the harder problems still ahead. There's also a real breaking change to budget for: code that calls synchronize on an Integer will start throwing an exception, since value objects have no monitor to lock on. Goetz himself doesn't expect JEP 401 to exit preview before JDK 29, the next LTS — so production teams have a long runway to plan the migration, not a deadline to scramble for.

Where this lands in the release calendar

JDK 26
Current release. JEP 401 is early-access only — not yet in any official preview build.
JDK 27
Expected September 2026.
JDK 28
Expected March 2027 — JEP 401's first official preview, after merging 197,000+ lines across 1,816 files into the mainline.
JDK 29
The next Long-Term Support release, expected September 2027 — likely still a preview feature, per Brian Goetz.
12
Years Since
Valhalla Began
197K+
Lines Added in
First Preview PR
1,816
Files Changed
for JEP 401
Lots of languages have, or are working toward, true value semantics — C# structs are one example. The hard part isn't the JVM. It's making this fit a model where classes have always meant identity and encapsulation.
— paraphrased from Brian Goetz, Java Language Architect, Oracle

Why this matters even if you never touch Valhalla code

Most developers will never write a value class by hand in the next two years. But every Java application — including the loan-processing pipelines and KYC microservices I build day to day — ultimately runs on the assumptions the JVM makes about memory and objects. When the platform gets cheaper at the lowest level, every layer built on top inherits some of that headroom, whether or not the team writing the business logic ever thinks about it.

Object identity in Java means "same address in memory," not "same value" — that gap is the root cause of most == confusion.
Value classes remove that identity on purpose, so equal values are simply equal — no caching quirks, no surprises.
This isn't a quick patch — it's a multi-year platform change, deliberately shipped slowly through previews so the ecosystem can adapt without surprises.
Performance, not just correctness, is the bigger motivation — fewer memory "hops" matters enormously at scale.