Java 26: A Bridging Release
Java 26 is here. It's not a game-changing, language-level overhaul, but rather the work of a meticulous engineer—focused on performance, modernization, and preparing the ground for Project Valhalla.
This release includes 10 JEPs (JDK Enhancement Proposals): 4 finalized features, 5 preview features, and 1 deprecation signal. Let's examine what they deliver.
Finalized Features
JEP 516: Ahead-of-Time Object Caching with Any GC
The problem: AOT object caching, introduced in Java 24, was locked to specific garbage collectors. It couldn't run with ZGC, heaps larger than 32GB, or with compressed object pointers disabled.
The solution: Instead of hardcoded memory addresses, the new design uses logical indexing. This abstraction decouples the cache from the GC's memory layout, enabling universal compatibility. It activates automatically with ZGC, large heaps, or -XX:-UseCompressedOops, and can be forced with -XX:+AOTStreamableObjects.
Impact: For serverless and CLI applications chasing extreme startup speed, AOT caching is critical. JEP 516 extends this benefit from G1 and Parallel GC to all modern collectors, making Java more competitive in the cloud-native space.
JEP 522: G1 GC—Improve Throughput by Reducing Synchronization
The gain: A direct throughput boost—5–15% for reference-intensive workloads, plus an additional ~5% on x64 due to reduced register pressure. The cost: about 2MB (0.2%) extra heap overhead per GB.
How it works: G1 uses a Card Table to track modified memory regions during concurrent marking. Application threads and GC threads previously contended for the same table, creating a synchronization bottleneck. JEP 522 introduces a second table: application threads write to one while GC threads process the other, eliminating direct conflict.
Impact: As the default GC, any G1 improvement benefits a vast number of applications. No code changes needed—simply upgrade the JDK.
JEP 517: HTTP/3 for the HTTP Client API
Why: HTTP/3 (built on the QUIC protocol) eliminates head-of-line blocking, establishes connections faster, and handles unstable networks better than HTTP/2. About one-third of websites already support it.
How to use: You must explicitly opt in. Four negotiation strategies are available:
// You need to explicitly request HTTP/3
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
// The client will attempt to negotiate HTTP/3 via ALPN or an HTTPS record
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Response protocol version: " + response.version());
Impact: The standard library now offers out-of-the-box HTTP/3 support. For common applications and teams reducing external dependencies, this is significant.
JEP 504: Remove the Applet API
The Applet API—a relic from browser-based Java—is gone. The java.applet package, java.beans.AppletInitializer, and javax.swing.JApplet have been removed.
Impact: Zero for most developers. Only those maintaining legacy code will notice.
Preview Features
JEP 530: Primitive Types in Patterns, instanceof, and switch (Fourth Preview)
Pattern matching now works directly with int, double, and boolean—no longer restricted to reference types.
// Preview feature, requires --enable-preview
static String formatPrimitive(Object obj) {
return switch (obj) {
// Match primitive types directly!
case int i -> String.format("int %d", i);
case long l -> String.format("long %d", l);
case double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> obj.toString();
};
}
// instanceof also supports primitive types
if (roomSize instanceof byte r) {
System.out.println("Room size fits in a byte: " + r);
}
This completes a major piece of the pattern matching puzzle, significantly enhancing expressiveness when dealing with mixed-type data (parsing JSON, processing database records).
JEP 525: Structured Concurrency (Sixth Preview)
Core principle: If a task splits into subtasks, it must wait for all of them to complete before continuing. This creates a clear parent-child hierarchy, eliminates thread leaks, and simplifies error handling and cancellation.
New in this round: An onTimeout() method; allSuccessfulOrThrow() now returns a List instead of a Stream.
// Preview feature, requires --enable-preview
void handleOrder() throws InterruptedException, ExecutionException, TimeoutException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> userFuture = scope.fork(this::findUser);
Future<Integer> orderFuture = scope.fork(this::fetchOrder);
scope.joinUntil(Instant.now().plusSeconds(5));
scope.throwIfFailed();
String user = userFuture.resultNow();
int order = orderFuture.resultNow();
System.out.println("Order " + order + " for user " + user);
}
}
Structured Concurrency represents the most significant evolution in Java's concurrency model since java.util.concurrent. It shifts the mental model from "managing threads" to "managing tasks." Six preview rounds reflect the care warranted by such a change; final form is approaching.
JEP 526: Lazy Constants (Second Preview)
A standard, safe, efficient way to define lazily initialized, immutable values. The new LazyConstant<T> class guarantees single initialization even under concurrent access and disallows null as a computed value. It also supports factory methods like List.ofLazy() and Map.ofLazy().
The classic "lazy initialization singleton" pattern is error-prone (thread safety, double-checked locking failures). Lazy Constants elevates this common design into a simple, reliable construct—especially valuable for high-performance libraries and frameworks.
Other Preview/Incubator Features
JEP 524: PEM-Encoded Cryptographic Objects (Second Preview) provides a standard API for parsing and generating PEM-formatted keys and certificates (PEMEncoder, PEMDecoder, PEM record), reducing reliance on third-party libraries like Bouncy Castle.
JEP 529: Vector API (Eleventh Incubator) leverages CPU SIMD instructions to accelerate vectorized computations, critical for Java's ambitions in high-performance computing and machine learning. Its final form depends on Project Valhalla's progress; no changes this round.
A Deprecation to Watch
JEP 500: Prepare to Make Final Mean Final
What: The JDK now warns when code attempts to modify a final field via reflection.
Why: The final keyword guarantees field immutability. The JVM and JIT compiler optimize based on this contract. Breaking it through reflection enables unpredictable behavior and blocks future optimizations.
Timeline: Currently a warning. In a future Java version, this will likely become an IllegalAccessException. Frameworks relying on this technique (older serialization libraries) need to adjust implementations soon. You can explicitly allow final field mutation per module with --enable-final-field-mutation=module1,module2.
Impact: This strengthens semantic consistency and opens greater optimization opportunities. Developers should audit codebases now to eliminate unsafe reflection.
Outlook
Java 26 is a classic bridging release. It finalizes AOT caching, G1 GC optimizations, and HTTP/3 support, delivering tangible benefits to existing applications. It continues refining heavyweight features—Structured Concurrency and Pattern Matching through multiple preview rounds—that will reshape Java programming.
More importantly, we can clearly see the shadow of Project Valhalla. Whether it's the long-incubating Vector API or the novel Lazy Constants, they await Valhalla's Value Types and Primitive Objects to unlock their full potential. Valhalla will be the most profound transformation of the Java platform since generics. Many features in Java 26 are laying the foundation for its arrival.
Upgrading to Java 26 is a smooth step forward. You'll get free performance improvements and modern network support, while gaining early access to powerful features that will define the next generation of Java programming.
References
- Java 26 is here! — Hanno's Blog