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.
⚠ The breaking changes concentrate in Java 8 → 17
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
Fast path for apps already on Java 17
# 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 verifyOpt-in virtual threads for blocking workloads
// 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
);
}AI-assisted migration workflow
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
- Migrate Google AutoValue to Java Records — Earlier step in the upgrade chain
- Java 17 to Java 21 Migration Guide — Upgrade path to same target
- Java 8 to Java 17 Migration Guide — Related version path
Official sources
- JDK 21 Migration Guide — docs.oracle.com (reliability 98%)
- JDK 21 (Project) — openjdk.org (reliability 98%)
- OpenJDK releases — openjdk/jdk (reliability 92%)
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.