eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – All Access – NPI EA (cat= Spring)
announcement - icon

All Access is finally out, with all of my Spring courses. Learn JUnit is out as well, and Learn Maven is coming fast. And, of course, quite a bit more affordable. Finally.

>> GET THE COURSE
Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Introduction

JDK 26 contains multiple resolved issues and over 1,000 enhancements. A major part of this work focuses on performance across the JDK libraries, garbage collectors, compiler, and runtime.

Some improvements require an API change or a JVM option. Others benefit existing applications as soon as they move to JDK 26. Together, these changes target faster startup, higher throughput, and better scalability.

In this tutorial, we’ll explore the most important performance improvements in JDK 26. We’ll also see how they affect application code and deployment choices.

2. JDK Library Improvements

We’ll start with the changes that very closely relate to application library code.

2.1. Lazy Constants

JEP 526 introduces Lazy Constants as a second preview. The LazyConstant API holds an immutable value that is computed only when it’s first requested.

Before JDK 26, delaying the creation of an expensive object often required a nullable field, a null check, and synchronization. For example, we could initialize a service only when it was first requested:

public final class Application {
    private static Service service;

    static synchronized Service service() {
        if (service == null) {
	    service = new Service();
	}

	return service;
    }

	private static final class Service {}
}

 

We can now express the same intent directly:

public final class Application {
    private static final LazyConstant<Service> SERVICE = LazyConstant.of(Service::new);
    static Service service() {
        return SERVICE.get();
    }
    private static final class Service {}
}

The first call to get() creates the service. Later calls return the same value. Initialization occurs at most once and remains safe when several threads race to access it. When a lazy constant is stored in a final field, the JVM can optimize repeated access in a way similar to a final constant. This gives us deferred work without permanently paying the usual synchronization cost.

Since this is a preview API, we need preview features at compile time and runtime:

javac --enable-preview --release 26 Application.java
java --enable-preview Application

2.2. Strings, Records, and Cryptography

JDK 26 reduces intermediate allocation and copying inside MemorySegment.getString(). This matters when an application frequently converts native or off-heap data into Java strings. Early benchmarks showed lower latency for all tested sizes, with the largest improvement for short strings.

Generated hashCode() methods for records now receive better type profiling. Record-heavy maps, sets, grouping operations, and deduplication can therefore gain throughput without source changes. The release also optimizes AES, ML-DSA, and elliptic-curve P-256 operations. These changes improve key setup, low-level arithmetic, and hardware-specific execution on supported processors.

There are smaller improvements as well. GZIPInputStream reads single compressed streams more efficiently, while Method.equals() it immediately succeeds when both references point to the same instance. The latter can help dynamic proxies, where method comparisons occur frequently during dispatch.

3. Garbage Collection and Startup Improvements

Garbage collection runs automatically, but it isn’t free. The JVM performs extra work whenever an application changes object references. It also prepares heap structures, classes, and commonly used objects during startup.

JDK 26 reduces both kinds of overhead. It makes G1 reference tracking less expensive, extends ahead-of-time caching to every garbage collector, and avoids preparing an unnecessarily large initial heap.

3.1. Lower G1 Synchronization Overhead

G1 divides the heap into many regions. During a collection, it can reclaim selected regions instead of processing the entire heap.

Objects in different regions can still reference one another. For example, an Order object in one region may reference a Customer object in another. G1 must remember this connection so that it doesn’t reclaim the customer while the order is still reachable.

G1 tracks these changes using a card table. The card table represents the heap as a collection of small areas called cards. When application code changes an object reference, a write barrier marks the corresponding card as dirty.

This write barrier runs for every relevant reference update:

order.setCustomer(customer);

Background refinement threads inspect dirty cards and record the references that cross region boundaries. Previously, application threads and refinement threads worked with the same card table. They needed additional synchronization to avoid interfering with one another. This synchronization made each write barrier more expensive. The cost became noticeable in applications that frequently create objects or update fields, such as caches, in-memory data stores, and request-processing systems.

JEP 522 introduces a second card table. Application threads write to the active table, while refinement threads process the other table. G1 swaps the tables when the active table needs refinement.

The two groups of threads can now perform most of their work independently. This reduces synchronization and makes object-reference updates cheaper. Published benchmarks showed throughput improvements of 5–15% in reference-heavy workloads. Workloads with fewer reference updates gained up to approximately 5%.

The second table requires additional native memory equal to roughly 0.2% of the heap. This is about 2 MB for every 1 GB of heap space. Applications already using G1 receive the improvement without code or configuration changes. However, the result depends on how frequently an application updates references. We should therefore verify the gain with a representative workload.

3.2. AOT Object Caching with Any GC

A Java application performs several operations before it can handle useful work. The JVM loads and links classes, verifies bytecode, and creates frequently used objects. Framework-based applications may repeat a large amount of this work on every start.

The ahead-of-time cache moves some of that work to an earlier training run. During training, the JVM records classes and heap objects used by the application. Later executions can load those prepared artifacts instead of creating everything again.

For example, the cache may contain Class objects together with their related strings and byte arrays. Reusing these objects can reduce both startup time and the time required to reach peak performance.

The difficulty is that garbage collectors don’t always represent object references in the same way. A cached object prepared for one collector may contain references that another collector can’t use directly. This previously limited AOT object caching to compatible garbage collectors.

JEP 516 adds a collector-independent representation for cached objects. Instead of storing references in a format tied to one collector, the cache can store objects in a form that the JVM converts while loading them. JDK 26 can therefore use two approaches. A GC-specific cache can be mapped directly into memory for a fast warm start. A GC-independent cache can be streamed into the heap and converted to the selected collector’s object format.

AOT object caching now works with every garbage collector, including ZGC. This allows applications to combine faster startup and warmup with ZGC’s low-pause behavior.

3.3. Smaller Default Initial Heap

The JVM also prepares an initial amount of heap memory at startup. We can set this value explicitly with -Xms or -XX:InitialHeapSize. When neither option is present, the JVM calculates a default.

Earlier releases based the default on 1.5625% of the machine’s physical memory. This is approximately one sixty-fourth of the available RAM.

Consequently, a high-memory server could receive a surprisingly large initial heap. On a machine with 256 GB of memory, 1.5625% represents roughly 4 GB before other JVM sizing rules are considered. A small service may not need anything close to that amount during startup. Preparing a larger heap involves additional initialization and metadata work. This can delay startup even though most of the initial space remains unused.

JDK 26 now uses MinHeapSize  as the default initial heap when no explicit size is configured. The JVM starts with a smaller heap and expands it as the application requires more memory.

This change reduces unnecessary startup work for applications that rely on the default heap settings. Applications that already provide –Xms or –XX:InitialHeapSize retain their configured behavior.

4. Compiler and Runtime Improvements

The C2 compiler can now optimize methods with very large parameter lists. Such methods previously remained on C1-compiled or interpreted paths. This change mainly helps generated code and frameworks that create unusually wide method signatures.

C2 also has a better cost model for SuperWord loop vectorization. Processing several values with SIMD instructions can be faster than scalar execution. However, packing, shuffling, and combining vectors also cost CPU time. The improved model helps C2 vectorize a loop only when the expected gain outweighs that extra work. No application changes are required.

Finally, virtual threads can unmount from their carrier threads while waiting for class initialization in common paths. Before JDK 26, this wait could pin a carrier and prevent it from running other virtual threads. The change improves scalability during bursts of class loading and reduces the risk of carrier starvation.

5. Measuring the Upgrade

Performance changes depend on allocation patterns, reference updates, startup behaviour, hardware, and the selected garbage collector. Therefore, an upgrade test should compare the same workload, JVM options, heap limits, and traffic profile on both JDK versions.

We can record Java Flight Recorder data during a representative run:

java -XX:StartFlightRecording=filename=jdk26.jfr,settings=profile \
	-jar application.jar

Useful comparisons include startup time, request throughput, allocation rate, GC pause distribution, and CPU consumption. For an AOT cache, the training workload should also cover the application’s normal startup path.

The best result is a repeatable improvement in the application’s own service-level metrics. A synthetic benchmark is useful for isolating a feature, but it doesn’t replace an end-to-end measurement.

6. Conclusion

In this article, we explored the main performance improvements in JDK 26. We looked at lazy constants, lower-allocation library code, G1’s reduced synchronization, broader AOT caching, smarter C2 decisions, and better virtual-thread behavior.

Most of these optimizations require little or no source change. By testing JDK 26 with production-like workloads, we can determine which improvements translate into meaningful gains for our applications.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)
guest
0 Comments
Oldest
Newest