Performance Improvements in JDK 26 - Inside.java
Inside Java<br>News and views from members of the Java team at Oracle
Newscast<br>Podcast<br>JEP Café<br>Sip of Java
dev.java<br>Newsletter<br>About
Performance Improvements in JDK 26
by Ana-Maria Mihalceanu, Per-Ake Minborg on June 9, 2026
JDK 26 is the latest feature release of the Java platform and comes with more than 2500 issues fixed, of which more than a thousand were enhancements. To give you a clearer view of the performance work happening across the Java platform, this article highlights a selection of notable performance related improvements in JDK 26, grouped into four major areas: JDK Libraries, Garbage Collectors, Compiler, and Runtime.
Enhancements in JDK Libraries
JEP 526: Lazy Constants (Second Preview)
The preview of the Lazy Constants API introduces java.lang.LazyConstant, an object that holds a single, unmodifiable value which is initialized on demand. After object initialization, the JVM can treat the value as constant, enabling optimizations similar to those available for final fields (constant folding), without the need to perform eager initialization in constructors or class initializers. You keep at-most-once initialization and thread safety, but you can initialize the object later, only if the value is actually needed, improving startup time and reducing unnecessary work.
The API first appeared in JDK 25 as Stable Values (JEP 502 and the version revised in JDK 26 incorporates substantial community feedback gathered from early adopters. The redesign renamed from StableValue to LazyConstant, methods (orElseSet, setOrThrow, trySet) got removed in favor of factories that take value-computing functions, and null is disallowed as a computed value to simplify and speed up the runtime model (aligning it with unmodifiable collections and ScopedValue style semantics). Discoverability is also improved by moving factories for lazy collections into List and Map (List.ofLazy, Map.ofLazy).
As shown in the snippet below, you can replace "mutable + null-check + synchronization" patterns with LazyConstant.of(() -> compute()), and call get() wherever you need the value.
SERVICE = LazyConstant.of(Service::new);\n\n static Service service() {\n return SERVICE.get();\n }\n}\n')">Copy<br>import java.lang.LazyConstant;
final class Application {<br>private static final LazyConstantService> SERVICE = LazyConstant.of(Service::new);
static Service service() {<br>return SERVICE.get();
Initialization is guaranteed to occur at most once, even under concurrent access: if multiple threads race, one wins and publishes the value safely. Under the hood, the mechanism still relies on JVM support for "stable" fields, so once the lazy value is set, repeated accesses can be optimized aggressively, provided that the LazyConstant itself is stored in a final field. This feature serves as a middle ground between eager initialization and peak performance: you can defer work out of startup, but once the value exists the JVM may optimize access similarly to a final constant.
JDK-8362893: Faster MemorySegment::getString
In performance discussions, allocation elision usually refers to the JIT compiler proves that a temporary object or array does not need to exist as a real heap allocation, so it removes the allocation or replaces it with cheaper operations. Some edge test cases discovered that MemorySegment::getString creates a temporary array internally, and that allocation is not optimized away.
Starting with JDK 26, string extraction from a MemorySegment benefits from an implementation that reduces intermediate allocation and copying when creating Java strings from memory segments. Early benchmark results showed lower latency across the tested string sizes and a particularly large improvement for short strings.
From a performance perspective, this enhancement means that MemorySegment::getString can exist on hot paths where native or off-heap data is frequently converted into Java strings. Reducing temporary allocation and copying overhead can lower per-call latency, reduce allocation pressure, and indirectly reduce garbage collection activity in workloads that perform many such conversions.
JDK-8366424: Missing Type Profiling in Generated Record Object Methods
JDK 26 also improves the performance of automatically generated hashCode() methods for record classes. Since records are commonly used as Map keys or Set elements, hashCode() performance can have a direct impact on application throughput.
With this change, record hashing is optimized to behave as efficient as manually written implementations. As a result, code that relies heavily on records for frequent lookups, grouping, indexing, or deduplication, can benefit from better throughput.
Cryptography Performance Improvements
JDK 26 includes several targeted performance improvements in cryptographic algorithms, including AES, ML-DSA, and Elliptic Curve P-256. These changes reduce unnecessary work in key setup, improve low-level...