Return to site

☕🪪 Java VALUE CLASSES: why losing IDENTITY can unlock PERFORMANCE #valhalla

Value classes get faster by throwing something away: identity.

September 26, 2026

🔸 TLDR

“The main optimization advantage of value classes is that we give up identity.”

But what exactly are we giving up?

▪️ Traditional Java objects have a unique identity independent of their state.

▪️ Preserving it often means maintaining a distinct object representation.

▪️ Value objects say: the state is the object.

▪️ That gives the JVM much more freedom to flatten, scalarize and sometimes eliminate allocations entirely.

JEP 401 makes this model a preview feature in Java 28.

So let's unpack identity 👇

🔸 1️⃣ WHAT IS OBJECT IDENTITY?

Answer

Identity is the JVM-visible fact that two objects are distinct even when all their fields are equal. With identity objects, == asks: “Are these references the same object?” Two equivalent objects can therefore still have different identities.

Code explanation

a and b contain exactly the same data.

But Java traditionally considers them two different objects.

Their state is equal.

Their identity is not.

#ObjectIdentity

🔸 2️⃣ HOW WAS THAT IDENTITY MATERIALIZED?

Answer

Traditionally, new creates a fresh identity object, typically materialized as a heap allocation reached through a reference/pointer, with object-header overhead. HotSpot may remove that allocation, but only when it can prove that identity is never observable.

Code explanation

The reference does more than let us reach the fields.

It also lets the runtime distinguish this particular object from every other object.

That distinct representation has a cost:

▪️ Heap allocation

▪️ Object header

▪️ Pointer dereference

▪️ GC work

▪️ Potentially poorer cache locality

#JavaMemory

🔸 3️⃣ WHY DID JAVA NEED IDENTITY?

Answer

Identity matters when objects are mutable. Two objects can have the same state now but evolve differently later. It also enables identity-sensitive operations such as synchronization and identity-based comparison.

Code explanation

Before the mutation, a and b looked identical.

But they were not interchangeable.

We need identity to know exactly which Counter we are modifying.

For mutable objects, identity has real semantic meaning.

#JavaObjects

🔸 4️⃣ WHAT HAPPENS WHEN WE GIVE IDENTITY UP?

Answer

A value object is defined only by its state. The JVM no longer has to preserve a unique object location, so it may flatten fields into their container or scalarize components into locals, stack slots or registers. new therefore does not necessarily imply a distinct heap object.

Code explanation

There is no observable distinction between two Point(10, 20) values.

So the JVM does not have to preserve:

reference → unique object → fields

It may instead work much more like:

x = 10

y = 20

That freedom is the optimization opportunity.

#ProjectValhalla

https://inside.java/2025/10/27/try-jep-401-value-classes/ )

🔸 5️⃣ WHAT DO WE LOSE WITHOUT IDENTITY?

Answer

You lose operations that require a unique object. You cannot synchronize on a value object, identical field states are not distinguishable with ==, and identity-dependent APIs such as weak references cannot be used. Value classes therefore suit immutable, interchangeable data.

Code explanation

You are making a semantic promise:

“I will never care which physical instance this is.”

So value classes are NOT simply:

normal classes + more performance

They deliberately remove capabilities.

That is why declaring a class value must first be a domain-model decision.

#ValueObjects

🔸 6️⃣ WHY IS LOSING IDENTITY SO INTERESTING FOR PERFORMANCE?

Answer

Because identity-free semantics are a guarantee, not an optimization guess. C2 no longer has to prove that identity is unobservable before scalarizing. This can remove allocations, pointer chasing and GC pressure while improving locality.

At source level:

But C2 may effectively work with:

instead of materializing a heap object for every transformation.

Code explanation

With an identity class, the optimizer has to prove:

“Nobody will observe the identity of this object.”

With a value class, you already gave the JVM that guarantee.

That is a much stronger starting point.

And JEP 539 adds another important guarantee: value-class fields are strictly initialized before they become observable.

For immutable final fields, that can even make larger flattened representations safe:

payload is final and strictly initialized.

The JVM knows it will never later be replaced with another value, giving it more freedom to flatten its representation.

#JVMPerformance

🔸 ⚠️ BUT VALUE DOES NOT MEAN “NEVER ALLOCATED”

There is an important subtlety.

Generic types are normally erased.

A value crossing an Object-typed or erased boundary may therefore need to be materialized into a real heap object.

Johan Sjölén's example showed exactly this: a megamorphic erased call caused materialization, while explicitly exposing the value types in the method descriptor reduced allocation from 192 bytes per invocation to zero.

So:

VALUE CLASS

≠

GUARANTEED FLATTENING

It means:

VALUE CLASS

=

MORE FREEDOM FOR THE JVM

#JavaPerformance

🔸 TAKEAWAYS

▪️ Identity means “which object?”, not “what value?”

▪️ Identity is essential for objects whose individual existence matters, especially mutable ones.

▪️ Immutable data often does not need that distinction.

▪️ Giving identity up allows the JVM to represent values without necessarily preserving a unique heap object.

▪️ That opens the door to FLATTENING, SCALARIZATION, better locality and fewer allocations.

▪️ JEP 539's strict initialization strengthens the guarantees needed for these representations.

▪️ But optimizations are never guaranteed: Object, generic and other abstraction boundaries can still force materialization.

▪️ So don't “value all the classes.”

Use a value class when the domain says:

these instances are immutable and interchangeable.

Performance is the bonus. 🚀

#Java #Java28 #JDK28 #ProjectValhalla #ValueClasses #ValueObjects #JEP401 #JEP539 #JVM #JVMPerformance #JavaPerformance #OpenJDK #SoftwareEngineering

https://johan-sjolen.github.io/post/compiler-sympathy/compiler-sympathy/

Go further with Java certification:

Java👇

https://bit.ly/javaOCP

Spring👇

https://bit.ly/2v7222

SpringBook👇

https://bit.ly/springtify

JavaFullstackBook👇

https://bit.ly/jfstack27