How Project Valhalla Changes JDK 28 in 2026
16 min read · 3,090 words
How does Project Valhalla work?
How Project Valhalla Changes JDK 28 in 2026
How Project Valhalla finally arrives in JDK 28 in 2026. Learn about value objects, primitive classes, and memory layout optimization for Java.
Ask Siliph
Answers from this article
Suggested questions
Key takeaways
- Memory Footprint Slash: Value classes run without the 16-byte object header overhead, flattening nested objects directly into arrays and stack frames.
- Performance Parity: Cache locality matches C++ and Rust, dramatically reducing CPU L1/L2 cache misses during massive data processing pipelines.
- Timeline Target: JDK 28, scheduled for General Availability in September 2026, will serve as the LTS baseline for fully integrated Valhalla features.
- Backward Compatibility: Existing APIs will migrate gracefully, but developers must identify classes using identity features like `synchronized` or identity hash codes.
In this article▼
HN trending (383 pts): Project Valhalla delivers value objects and primitive classes in JDK 28 by late 2026, eliminating the memory overhead of object headers to slash Java heap usage by up to 70% without sacrificing object-oriented design.
Key takeaways
The Trillion-Byte Problem: Why Java's Memory Footprint Limits Cloud Scalability in 2026
For over two decades, Java developers accepted a fundamental tax: every single object created on the heap carried a hidden toll. On standard 64-bit virtual machines, an empty object requires 16 bytes of metadata just to exist. When you scale this across cloud microservices handling millions of events per second, the financial drain is obvious. Companies routinely provision massive cloud instances not for CPU performance, but simply to house bloated object heaps and survive the inevitable garbage collection pauses.
Project Valhalla fixes this design compromise at the JVM level. Rather than forcing developers to write complex, unmaintainable primitive array code, JDK 28 introduces the concept of "codes like a class, works like an int." This is not a minor syntax upgrade; it is a structural revolution. It completely redefines how the Java Virtual Machine represents data in memory, aligning language semantics with modern CPU memory architectures.
Who this affects right now
How Value Classes and Primitive Classes Redefine JVM Memory Layout
To understand Valhalla's power, you must grasp the difference between identity objects and value objects. Historically, every Java object has had a unique identity. Two objects with the exact same fields are distinct instances in memory. Valhalla splits this model.
Value classes declare that their instances care only about their state, not their identity. This allows the JVM to make better storage layout. In many cases, it eliminates the object reference entirely, storing the payload inline.
| Feature | Classic Identity Objects (Current) | Value Objects (JDK 28) | Primitive Objects (JDK 28) |
|---|---|---|---|
| Has Object Identity? | Yes | No | No |
| Can Be Null? | Yes | Yes | No (Default state initialized) |
| Memory Layout | Indirect reference via pointers | Flat stack allocation possible | Inlined directly in memory |
| Supports Synchronization? | Yes | No (Throws runtime exception) | No (Compilation error) |
| Thread-Safe by Default? | No (Requires explicit locking) | Yes (Immutable fields) | Yes (Immutable fields) |
| Heap Overhead | Full 16-byte header | Reduced or eliminated header | Zero header overhead |
The Real-World Math: 10 Million Coordinates Analyzed
Let us calculate the concrete memory savings using standard heap structures. Imagine a simple GPS tracking service processing an array of 10 million coordinate points, where each point contains two 64-bit double values (latitude and longitude).
Under the traditional Java memory model, your application creates 10 million distinct objects. The array itself contains references (pointers) to those objects. In a 64-bit JVM with compressed oops (ordinary object pointers) disabled, each pointer takes 8 bytes. Each coordinate object requires a 16-byte object header, plus 16 bytes for the two double fields (8 bytes each).
Let us compile the raw math:
Under Project Valhalla's primitive classes, the array contains the raw values directly. Pointers and object headers are completely eliminated from the heap. The array becomes a single contiguous block of memory holding the double values.
Let us look at the Valhalla calculation:
This simple architectural shift instantly saves 240 megabytes of RAM—a massive 60% reduction. It also ensures CPU cache lines load multiple contiguous coordinate records simultaneously, speeding up data access times by orders of magnitude. Just as a precise /blog/tools/paycheck-calculator breaks down every deduction, analyzing this heap allocation lets you predict your platform's operational costs. When scaling to billions of records, these efficiencies translate to tens of thousands of dollars saved on your cloud infrastructure invoice, much like running optimizations through an /blog/tools/emi-calculator helps model long-term financial liabilities.
5 mistakes people make
The Bytecode Bridge: Navigating the L-type and Q-type Migration
The JVM must handle legacy bytecode and modern value types simultaneously. To solve this, Valhalla introduces new descriptors at the bytecode level. Class files will use `Q-types` for direct, inlined instances and `L-types` when referencing nullable or polymorphic versions of those same classes.
I have seen enterprise migrations fail because engineering teams treated platform upgrades like simple dependencies. Do not make that mistake. When JDK 28 lands, your build tools and profiling agents will need to interpret these new bytecode structures correctly. Start testing preview builds now to catch compiler anomalies early.
What to do today
What experts and regulators say
The OpenJDK core maintenance teams emphasize that Valhalla is the most complex change ever introduced to Java. Language architect Brian Goetz has repeatedly stressed that the goal is not to force developers to think about memory layouts constantly, but rather to allow library writers to deliver high-performance tools that feel like standard Java. Enterprise software standard bodies and major cloud providers are already optimizing their virtualization systems to exploit these runtime memory improvements as soon as JDK 28 reaches production status.
What is the timeline for Project Valhalla?
Project Valhalla features have been delivered in increments through preview JEPs in recent JDK releases. The complete, standardized graduation of value objects and primitive classes is slated for the JDK 28 release in September 2026, which will serve as a foundational target for production systems.
What is the difference between a value class and a record?
A record is a structural way to define data carriers with automatic compiler-generated getters, equals, and hashcode methods. A value class focuses purely on memory layout, telling the JVM that the instances lack identity, allowing them to be optimized and flattened in memory.
Can a value class be mutable?
No, value classes are strictly immutable. Because they lack identity, their fields must be marked as final, preventing side-effect mutations and making them inherently thread-safe across parallel processing pipelines.
Will my legacy libraries break when I upgrade to JDK 28?
Standard libraries that do not rely on identity checks, serialization tricks, or synchronized blocks will continue to work without modification. However, older frameworks that rely on deep reflection and identity validation will require updates to support value types.
How does Valhalla reduce garbage collection overhead?
By inlining data directly into arrays and stack frames, Valhalla drastically reduces the sheer count of individual objects allocated on the heap. Fewer objects mean the garbage collector has fewer references to trace and clean up, resulting in shorter pause times.
Do value objects support polymorphism and inheritance?
Value classes can set up interfaces, but they cannot extend other identity classes. This restriction exists because abstract class hierarchies often rely on object identity to manage state, which violates the core performance promises of Valhalla.
What happens if I use the double equals operator on value objects?
Using the `==` operator on value objects compares their actual state values rather than their memory addresses. If all fields inside two distinct value object instances match, the comparison evaluates to true, aligning with mathematical expectations.
How do primitive classes handle default values?
Unlike reference types that default to null, primitive classes default to a state where all their internal fields are set to their respective default values, such as zero for numeric fields and false for booleans.
Editorial note
This guide is provided for educational and architectural planning purposes only. Specific performance gains, JEP specifications, and final release timelines are subject to change by the OpenJDK governing body prior to the GA release of JDK 28.
Deep Dive into JVM Internals: L-World vs Q-World
To truly understand how Project Valhalla fundamentally alters the Java Virtual Machine, one must look at the bytecode level. Historically, every object reference in Java bytecode was represented by an `L-descriptor` (e.g., `Ljava/lang/String;`). This descriptor tells the JVM that the value is an object reference, requiring pointer indirection, supporting nullability, and carrying a distinct object identity.
Under Project Valhalla, the JVM introduces a parallel universe: the `Q-descriptor` (e.g., `Qcom/example/Point;`). Q-descriptors represent direct, inline values. They are non-nullable, identity-free, and passed by value. When the JVM encounters a Q-descriptor, it knows it can pass the fields of the object directly in CPU registers or on the execution stack, bypassing the heap entirely. This separation allows the compiler to compile code into two distinct calling conventions: one optimized for references (L-type) and one optimized for raw speed and low memory footprint (Q-type).
The Mechanics of Memory Flattening
Modern CPUs are incredibly fast, but they are frequently starved for data due to memory latency. When a CPU core requests data from main memory, it fetches an entire cache line (typically 64 bytes). In classical Java, an array of objects `Point[]` is actually an array of pointers to `Point` objects scattered across the heap. Accessing each element requires pointer chasing, which misses the CPU cache and results in expensive memory stall cycles.
Valhalla introduces the concept of "memory flattening." An array of value classes (using the primitive modifier or optimized by the JVM) is laid out contiguously in memory, much like an array of structures in C, C++, or Rust.
This contiguous allocation ensures that when the CPU fetches a cache line, it loads multiple elements at once, leading to an order-of-magnitude increase in cache hits and memory throughput.
Migration Strategies for Library Maintainers
For enterprise framework developers and library maintainers, migrating to JDK 28 requires careful preparation. Although the JVM handles much of the transition transparently, several critical practices must be adopted:
Performance Benchmarks: What to Expect
Below is an architectural projection comparing memory performance and behavior across different class types under JDK 28:
| Feature or Structure | Classical Reference Class | Value Class (Identity-Free) | Primitive Class (Flattened) |
|---|---|---|---|
| Memory Overhead | 16-byte object header + padding | 0-byte header (when flattened) | 0-byte header (always inlined) |
| GC Tracing Overhead | High (every instance is a heap node) | Low (no reference tracing needed) | None (inlined into parent structure) |
| Cache Locality | Poor (pointer indirection) | Good (on stack / CPU registers) | Excellent (contiguous cache lines) |
| Null Support | Yes (default behavior) | Yes (via reference projection) | No (defaults to zero-state) |
The Evolution of Generics: Parametric Polymorphism
Historically, Java's generics suffered from "type erasure." A `List<Integer>` is erased to `List<Object>`, forcing the boxing of primitive `int` values into heap-allocated `Integer` wrappers.
Valhalla’s secondary goal is to enable generic specialization. With the introduction of value and primitive classes, the compiler and JVM can specialize generic classes at runtime. This means `List<Point>` or `List<int>` can exist without boxing, allocating a flattened, contiguous array under the hood. This represents the ultimate goal of Java performance: the expressiveness of Java's type system combined with the bare-metal performance of C++ templates.
How do Q-descriptors change the underlying Java bytecode?
Classical bytecode instructions like `aload`, `astore`, and `areturn` have historically operated on object references. With Valhalla, the bytecode interpreter and JIT compiler are updated to handle Q-types. New opcodes and metadata attributes are introduced in the class file format to describe inline types. This allows the JIT compiler to make better register allocation, passing Q-type fields directly in CPU registers rather than pushing a reference pointer onto the execution stack.
Can value classes be used as keys in a ConcurrentHashMap?
Yes, value classes can be used as keys. Because value classes do not have an identity, their `hashCode()` and `equals()` methods are derived entirely from their field values. If two value objects have identical fields, they will produce the exact same hash code and return true for `equals()`. This makes them highly predictable and safe for use as keys in thread-safe collections.
How does Project Valhalla affect GraalVM Native Image compilation?
Project Valhalla is highly beneficial for Ahead-Of-Time (AOT) compilers like GraalVM. Since the compiler knows statically that certain classes have no identity and are flattened, it can perform aggressive escape analysis, dead-code elimination, and loop unrolling. This leads to significantly smaller native executable sizes and faster startup times, as the native runtime does not need to allocate heap space for ephemeral data carriers.
What is the difference between Bucket 1, Bucket 2, and Bucket 3 values?
The OpenJDK design documents often refer to three distinct categories of types. Bucket 1 represents classic identity classes (which have identity, support locking, mutability, and nullability). Bucket 2 represents value classes (identity-free, immutable, support nullability via reference projections). Bucket 3 represents primitive classes (identity-free, immutable, non-nullable, fully flattened, defaulting to a zero-state).
Will serialization break for value classes?
Standard Java serialization will be supported, but with structural modifications. Because value classes have no identity, the traditional serialization graph-tracking mechanisms (which prevent circular references) are bypassed for these types. Serialization frameworks like Jackson, Kryo, and Protocol Buffers are updating their engines to deserialize value classes directly by instantiating their fields rather than relying on reflection-based field-setting on empty instances.
How does the JVM handle array covariance for value types?
Historically, Java arrays are covariant (e.g., `String[]` is a subtype of `Object[]`). This covariance requires runtime checks when storing elements to prevent type pollution. With flattened arrays of value or primitive types, covariance is not possible because the memory layout is different. The JVM treats `Point[]` as a flat block of memory, which cannot be cast to `Object[]` without copying or wrapping.
Can we use reflection to mutate fields of a value class?
No. Reflection-based mutation (using `Field.setAccessible(true)` and `Field.set()`) on value classes will throw an `IllegalAccessException` or `UnsupportedOperationException` at runtime. Since value classes are immutable and can be stored entirely in CPU registers or inlined in flattened arrays, there is no physical heap address where a mutated value could be written.
What are the specific compiler flags available in JDK 28 to test Valhalla features early?
While JDK 28 aims for General Availability, early-access builds work with flags like `--enable-preview` and specific internal VM arguments such as `-XX:+EnableValhalla`. These flags enable the javac compiler to parse the `value` and `primitive` modifiers and configure the HotSpot JVM to load and make better class files containing Q-descriptors and flattened layout configurations.
Editorial note
This guide is provided for educational and architectural planning purposes only. Specific performance gains, JEP specifications, and final release timelines are subject to change by the OpenJDK governing body prior to the GA release of JDK 28.
Related Siliph resources
When you need to handle documents, try Merge PDF Online Free, Compress PDF Online Free, Compress PDF to 100KB on Siliph — free, secure, and browser-based.
Anupam Pradhan
Founding Editor
Founder of Siliph. 14+ years covering fintech, document workflows, and digital banking across India and global markets.
More from this author →