VVersions.dev

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.

Version upgradeDifficulty: hardEffort: 2–10 days depending on the starting LTShigh risk

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

VersionReleasedLTSSupport status
Java 82014-03-18YesSecurity-only
Java 112018-09-25YesMaintenance
Java 172021-09-14YesActive
Java 212023-09-19YesCurrent
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 verify
Opt-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
    );
}
Migration executionAI-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.

Official sources

  • Migration guideJDK 21 Migration Guidedocs.oracle.comreliability 98%

    Backs the breaking-change and migration-step claims.

  • Official docsJDK 21 (Project)openjdk.orgreliability 98%

    Backs the breaking-change and migration-step claims.

  • GitHub releaseOpenJDK releasesopenjdk/jdkreliability 92%

    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.