Upgrade to Java 21
Java 21 is the recommended LTS target. The hard part of the jump is concentrated between Java 8 and 17 — the module system, removed Java EE modules, and strong encapsulation of internals. From 17 the step to 21 is small and mostly additive.
Last verified · Updated May 22, 2026
Java 21 is the recommended LTS. Most breakage comes from the Java 8 → 17 portion of the jump — the module system, removed javax.* Java EE modules, and strong encapsulation of JDK internals — not from your business logic. Once you are on 17, the step to 21 is small and additive (virtual threads, pattern matching, sequenced collections).
Who should upgrade
- Apps on Java 17 wanting virtual threads, record patterns, and sequenced collections.
- Teams on Java 8/11 that need a supported LTS runtime and modern language features.
- Anyone blocked by libraries that now require a newer baseline JDK.
Who should wait
- Apps pinned to a critical dependency that lacks a Java 17+ release.
- Builds that rely on removed Java EE modules with no maintained replacement yet identified.
What changed across the jump
- JPMS module system with strong encapsulation of JDK internals (sun.misc.Unsafe, sun.*).
- Java EE / JAXB / JAX-WS modules removed from the JDK — now external dependencies.
- Language features: records, sealed classes, switch expressions, text blocks, var.
- G1 is the default garbage collector.
- Java 21 adds virtual threads, pattern matching for switch, record patterns, sequenced collections, and generational ZGC.
Strong encapsulation and the removed Java EE modules are the two things most likely to break a build. Inspect for sun.misc.Unsafe / internal-API access and for JAXB/JAX-WS usage before bumping the toolchain — both fail at runtime, not always at compile time.
Java LTS support matrix
| Version | Released | LTS | Support status |
|---|---|---|---|
| Java 8 | 2014-03-18 | Yes | Security-only |
| Java 11 | 2018-09-25 | Yes | Maintenance |
| Java 17 | 2021-09-14 | Yes | Active |
| Java 21 | 2023-09-19 | Yes | Current |
Recommended upgrade path
# 1. Point the build toolchain at JDK 21$ export JAVA_HOME=$(/usr/libexec/java_home -v 21)$ java -version# 2. Maven: set the compiler release flag$ ./mvnw versions:set-property -Dproperty=maven.compiler.release -DnewVersion=21# 3. Build + test on the new JDK$ ./mvnw clean verify// Java 21: switch executors to virtual threads where I/O dominates
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
requests.forEach(req ->
executor.submit(() -> handle(req)) // blocking I/O is now cheap
);
}Upgrade this project to Java 21. First inspect for the high-risk changes (sun.misc.Unsafe / internal-API access, removed javax.* Java EE modules, and outdated Lombok/ASM/ByteBuddy). Then point the build toolchain at JDK 21, set the compiler release flag to 21, add explicit dependencies for removed javax.* modules, bump bytecode-manipulating libraries, and add the minimum --add-opens/--add-exports only where required. Run the build and tests after each step and report before continuing.
Safety: Incremental edits only. Prefer fixing encapsulation violations over blanket --add-opens. Pause for review after the toolchain bump and after each dependency change.
PR review checklist
- Build toolchain and CI both target JDK 21
- maven.compiler.release / Gradle toolchain set to 21
- Removed javax.* modules added as explicit dependencies
- Lombok and other bytecode libraries bumped to JDK 21-compatible versions
- --add-opens/--add-exports limited to the minimum required, with a comment explaining each
- No InaccessibleObjectException or illegal-access warnings at startup
Rollback strategy
- Keep the toolchain bump, dependency additions, and encapsulation flags in separate commits.
- Revert JAVA_HOME and the compiler release flag to fall back to the prior LTS.
- Hold the upgrade behind a release branch until the full test suite is green on the target JDK.
Common errors
- InaccessibleObjectException — strong encapsulation blocked reflection; add a scoped --add-opens or fix the access.
- NoClassDefFoundError: javax/xml/bind/* — add JAXB as an explicit dependency.
- Lombok failing to compile — bump to a JDK 21-compatible release.
Related migration paths
Official sources
Backs the breaking-change and migration-step claims.
Backs the breaking-change and migration-step claims.
Backs the breaking-change and migration-step claims.
Frequently asked questions
Is upgrading from Java 8 to 21 a hard upgrade?
The difficulty lives in the 8 → 17 portion: the module system, removed Java EE modules, and strong encapsulation of internals. Splitting the work into 8 → 17 and then 17 → 21 makes each step reviewable. The 17 → 21 step itself is small and mostly additive.
Do I have to adopt virtual threads or the module system?
No. Virtual threads are opt-in — existing thread-per-request code keeps working. You also do not need to add module-info.java; most applications upgrade on the classpath and only deal with strong encapsulation where reflection into JDK internals is involved.