01Triage
Which Java 21 features do you adopt during the upgrade?
Upgrading the runtime and adopting a language feature are separate jobs. Do the first now and the second later. The upgrade itself is described on the Java upgrade page.
| Feature | JEP | Touches | Adopt during upgrade? |
|---|---|---|---|
| Virtual threads | 444 | Runtime and concurrency design | No. Test first, adopt per service |
| Generational ZGC | 439 | Operations and tuning | Optional. Benchmark on your workload |
| Record patterns | 440 | Source code | No. Use in new code |
| Pattern matching for switch | 441 | Source code | No. Use in new code |
| Sequenced collections | 431 | Library API | Optional. Small, safe additions |
| Dynamic agent loading warning | 451 | Tools and agents | Yes. Check your APM and test agents |
02Concurrency
Virtual threads: what changes and what does not
Virtual threads are lightweight threads managed by the JVM. They let a server hold many blocked requests without one operating system thread each. Code written in the plain blocking style scales better without moving to reactive libraries.
They do not make CPU-bound code faster. They help when threads mostly wait on a database, a file or another service.
Two checks matter in a legacy codebase. First, code that holds a synchronized lock while blocking could pin the carrier thread in Java 21; that was relieved in a later release. Second, thread-local caches sized for a small pool can grow when you have many thousands of virtual threads. A connection pool is still a hard limit: virtual threads do not create more database connections.
03Language
Record patterns and pattern matching for switch
Pattern matching for switch lets a switch test the type of a value and bind it in one step. Record patterns take that further and unpack a record into its parts. Together they replace chains of instanceof checks and casts.
A legacy application gets nothing from these until someone rewrites code to use them. Recipes can find simple instanceof chains, but a person should choose where the new form reads better. Treat this as cleanup after the upgrade, not part of it.
04Library
Sequenced collections
Sequenced collections add methods such as getFirst, getLast and reversed to collections that have a defined order, including List, Deque and LinkedHashSet. Before Java 21 the way to get the last element differed by type.
One compatibility note: adding methods to interfaces can clash with methods in your own subclasses or in libraries that already define getFirst or getLast. The compiler tells you. It is a small but real source of build errors when moving to 21.
05Operations
Generational ZGC
ZGC is a low-pause garbage collector. In Java 21 it gained a generational mode, which collects young objects more often and can reduce the memory and CPU cost of collection. In Java 21 you enable it with a flag; later releases changed the default.
Do not switch collectors because a list says so. Measure pause time, throughput and memory on a copy of your own workload, against the collector you run now.
06Our test
What are we testing on a real upgrade?
We are running a sample-repository upgrade to Java 21 with OpenRewrite recipes, AI-assisted tools and engineer review. We will record which compile errors, runtime flags and test failures appear and which of them the recipes fixed. We are running this test; results will be published here.
07Next step
How does Ace8 help?
Ace8 is the AI-assisted application modernization practice of AceMQ. We move estates onto a supported Java version and leave feature adoption for the teams that own the code.
To see what a Java 21 upgrade would involve for your applications, start with the modernization assessment. See also Java 25 LTS if you are choosing between the two.