In a landmark development for the Java ecosystem, the OpenJDK community has officially integrated JEP 401: Value Objects (Preview) into the mainline codebase for JDK 28. This integration marks a pivotal milestone in the multi-year effort known as "Project Valhalla," aimed at modernizing Java’s memory model to better align with the performance demands of contemporary hardware. By introducing "identity-free" classes, JEP 401 challenges the fundamental assumption that every object must possess a unique, mutable identity, potentially unlocking unprecedented levels of memory efficiency and execution speed.
The Core Concept: Redefining the Object
Since its inception, Java has treated all objects as having "identity"—a distinct, observable existence defined by their memory address and the ability to be locked via synchronization. While this model is robust for complex, stateful systems, it imposes a significant "identity tax" on data-carrying types like points, complex numbers, or currency representations.
JEP 401 introduces the value modifier. When a class is declared as a value class, it signals to the JVM that the object is defined solely by the data it carries rather than its location in memory. This shift allows the JVM to treat these objects more like primitives, enabling optimizations such as flattening (storing data directly in fields or arrays without pointers) and scalarization (breaking an object down into its constituent fields for register allocation).
Strict Construction and Syntax
The transition to value classes requires a shift in how developers write constructors. Under JEP 401, a value class mandates that all instance fields are implicitly final. Furthermore, the JVM enforces strict initialization: every field must be explicitly assigned before the constructor completes and the object becomes observable.
value class Point
private int x; // Implicitly final
private int y;
public Point(int x, int y)
this.x = x; // Mandatory assignment
this.y = y; // Mandatory assignment
public int x() return x;
public int y() return y;
This ensures that once a value object is created, its state is immutable and fully initialized, eliminating a broad category of thread-safety and state-corruption bugs.
Chronology of a Paradigm Shift
The journey to JDK 28 was neither quick nor simple. The discussions surrounding Project Valhalla date back nearly a decade, originating in the valhalla-dev mailing list. The evolution can be categorized into three distinct phases:
- The Exploratory Phase (2014–2018): Brian Goetz and the OpenJDK architects identified that the "Object-Oriented Everything" model was hitting a wall regarding cache locality. Research began into "Value Types," a concept inspired by structs in languages like C# and C++.
- The Refinement Phase (2019–2023): The focus shifted from merely introducing structs to integrating them seamlessly into the existing Java type system. This period saw the creation of JEPs related to "Universal Generics" and "Strict Field Initialization" (JEP 539), which provide the necessary bytecode verification for the new memory model.
- The Integration Phase (2024–Present): With the introduction of JDK 28, the theoretical concepts have finally solidified into a functional, albeit preview, implementation. The decision to keep it as a preview feature underscores the conservative, long-term approach the Java team takes toward breaking changes in the object model.
Supporting Data and Technical Implications
The primary goal of JEP 401 is to eliminate the overhead of object headers and pointers. In the current Java model, every object carries a header (typically 12–16 bytes) and requires an indirection through a reference pointer.
The == Operator Transformation
Perhaps the most jarring change for developers is the evolution of the == operator. Previously, == checked for reference equality (do these two variables point to the same memory address?). For value objects, == now checks for "value equality." If two Point objects have the same x and y values, p1 == p2 will return true, even if they were created at different times.
- Recursion: If a value object contains other reference-typed fields, the
==operator performs a recursive check, ensuring that deep equality is maintained without requiring a developer to manually overrideequals()for every comparison. - Backward Compatibility: Identity objects (standard
classdeclarations) maintain their original behavior. AString, for instance, remains an identity class, ensuring that legacy codebases do not experience sudden, widespread logical failures.
The JVM Optimization Payoff
The performance benefit is not guaranteed as a "set-and-forget" switch; rather, it is an optimization opportunity. The JVM may:
- Flattening: Store value objects inline in memory arrays, drastically reducing memory footprint and improving cache hit rates.
- Scalarization: Instead of allocating an object on the heap, the JIT compiler may decompose it into local variables, essentially removing the object allocation entirely.
However, the JEP notes that during the "warmup" phase—before the JIT compiler has fully optimized the hot code paths—the JVM may fall back to standard heap allocation. Developers should view this as a long-term architectural gain rather than a "silver bullet" for immediate performance spikes.
Official Responses and Industry Outlook
The OpenJDK architects have been vocal about the trade-offs. Brian Goetz, Java Language Architect at Oracle, has frequently noted that the "identity" of an object is often an accidental byproduct of Java’s history rather than a requirement for the data it represents.
Industry reaction has been largely positive, with high-frequency trading (HFT) firms and big-data processing groups—who have long struggled with GC pressure caused by millions of short-lived objects—expressing interest in the feature. However, there is a clear warning for the general developer population: the value modifier is not intended for all classes. It is a specialized tool for performance-critical data structures.
Implications for the Ecosystem
The integration of JEP 401 carries profound implications for library authors and maintainers of the JDK itself.
Migration of JDK Classes
With the preview flag enabled, several fundamental JDK classes—including the primitive wrappers (Integer, Long, etc.) and LocalDate—are being transitioned into value classes. This creates a ripple effect:
- Breaking Changes: APIs that rely on locking objects (e.g.,
synchronized(myInteger)) will fail. - IdentityException: Attempting to create a
Reference(likeWeakReference) to a value object will throw anIdentityException, as value objects do not have a stable identity that can be tracked.
The Role of equals()
A critical takeaway from the documentation is that == for value objects is not a replacement for equals(). Developers must continue to use equals() for business logic comparisons, as the internal data structure of a value object may not always represent the logical equality of the domain entity it models.
Security Considerations
The shift in how == and identityHashCode function introduces new security vectors. Because these operators can now expose the internal state of a value object, developers must be cautious about using these objects in security-sensitive contexts where internal data might be inferred by side-channel attacks. Furthermore, the recursive nature of value comparison means that an attacker could potentially construct a deeply nested, recursive value structure that causes an unbounded execution time during a comparison operation.
Conclusion: A New Era for Java
JEP 401 represents the most significant change to the Java object model since the introduction of Generics in Java 5. By decoupling the concept of an "object" from the concept of "identity," Java is moving closer to the hardware-centric performance levels seen in systems languages while retaining the safety and maintainability that define the Java platform.
As we move into the era of JDK 28 and beyond, the value modifier will likely become a standard tool in the performance engineer’s toolkit. While the transition period—marked by the need for the --enable-preview flag and the potential for compiler warnings—will require diligence, the long-term potential for a more efficient, cache-friendly Java is clear. The project serves as a reminder that Java is a living, evolving language, capable of reinventing its core principles to meet the challenges of the next generation of computing.








