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 25 is now the current LTS
As of September 2025, Java 25 (LTS) is the recommended target for new and modernizing projects, and Oracle Premier Support for Java 21 runs through September 2028. This page still covers the Java 8/11/17 → 21 jump accurately; if you are starting fresh, see Upgrade to Java 25 and the Java 21 → 25 / Java 17 → 25 paths.
Java 21 is a supported LTS (Oracle Premier Support through September 2028). 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). The current LTS target is Java 25.
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 | Oracle Premier Support until | Support status |
|---|---|---|---|---|
| Java 8 | 2014-03-18 | Yes | March 2022 | Security-only |
| Java 11 | 2018-09-25 | Yes | September 2023 | Maintenance |
| Java 17 | 2021-09-14 | Yes | September 2026 | Active (Premier ending) |
| Java 21 | 2023-09-19 | Yes | September 2028 | Active |
| Java 25 | 2025-09-16 | Yes | September 2030 | Current LTS |
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 17 to Java 25 Migration Guide — Related version path
- Java 21 to Java 25 Migration Guide — Next step forward
- Java 8 to Java 17 Migration Guide — Related version path
- Upgrade to Java 25 — 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.