Cloud Builders / Articles / Java 25 LTS

Java 25 LTS: New Features and a Practical Migration Guide from Java 21

Java 25 cloud native runtime

On September 16, 2025, Oracle released Java 25 — the first LTS since Java 21, two years in the making. Eighteen JEPs, at least eight years of vendor support, and a distinct lean toward cloud-native scenarios. By the summer of 2026, teams have accumulated the first real production experience with it, and the migration conversation has become concrete: where Java 25 actually pays off, and where you can afford to wait.

The clock is ticking: why the topic became urgent

Formally, three interim releases sit between Java 21 and Java 25 — 22, 23, and 24. Most enterprise teams skipped them: a short support window rarely justifies a migration. So Java 25 effectively packages two years of evolution at once — around 66 JEPs across all the interim releases. Some features were promoted from preview, some were refined based on feedback, and some were introduced from scratch.

The second factor is the calendar. Oracle's free updates for Java 21 end on September 16, 2026. After that — paid subscription or a switch to alternative builds (Temurin, Corretto, Zulu). Java 25, by contrast, gets a support window of at least eight years. For teams planning their release cycle two or three quarters ahead, this is no longer a "someday" question.

Key JEPs worth looking at first

The full list of 18 JEPs is on openjdk.org. Below are the ones most commonly cited when teams discuss upgrading from Java 21.

JEP Name Status in Java 25 Who needs it
506 Scoped Values Final Microservices using ThreadLocal with virtual threads
505 Structured Concurrency Fifth preview Parallel calls to external APIs
521 Generational Shenandoah Final Services with large heaps and pause-time SLAs
519 Compact Object Headers Final Anything memory-bound in heap-heavy workloads
515 AOT Method Profiling Final Short-lived pods, serverless, batch jobs
513 Flexible Constructor Bodies Final Library authors, DDD models
512 Compact Source Files Final Prototypes, teaching, scripting

The table isn't a substitute for reading the release notes, but it sets the right order of priorities: first the JEPs that deliver wins without code changes (519, 521, 515), then those that simplify concurrency (506, 505), and finally the syntactic sugar.

Structured Concurrency and Scoped Values: life after virtual threads

Virtual Threads in Java 21 solved the "a million connections on a single JVM" problem. But they came with two awkward side effects: how to cancel a group of parallel tasks cleanly, and what to do with ThreadLocal, which turns into a memory leak when you have millions of threads.

Structured Concurrency (fifth preview in Java 25) offers a clear model: tasks inside the same scope complete together. Cancellation, errors, and timeouts propagate to every fork without manual join juggling. For services that fan out three to five external calls per request, this simplifies the code and eliminates an entire class of bugs.

Scoped Values in Java 25 are final. This is an immutable replacement for ThreadLocal, designed with virtual threads in mind: no memory leaks, an explicit lifetime, and correct context propagation to child tasks. In practice, request IDs, tenant info, and security contexts stop being a headache.

Example: three parallel calls with a shared timeout

Previously, calling three services "in parallel with a single timeout" required an ExecutorService, manual Future juggling, and custom cancellation logic. In Java 25, the code fits in about ten lines:

try (var scope = StructuredTaskScope.open()) {
    var user    = scope.fork(() -> fetchUser(id));
    var orders  = scope.fork(() -> fetchOrders(id));
    var billing = scope.fork(() -> fetchBilling(id));

    scope.join().throwIfFailed();

    return new Profile(user.get(), orders.get(), billing.get());
}

If any of the calls fails or exceeds the timeout, the scope automatically cancels the rest. There's no more manual Future management, and the stack trace on failure stays readable.

StructuredTaskScope parallel API calls

Runtime, memory, and startup: where Java 25 saves cloud costs

The main runtime improvements in this release deliver infrastructure savings without a single line of new code:

  • Compact Object Headers (JEP 519). Object header size shrinks from 12–16 bytes down to 8. On heap-heavy services (caches, ORMs, message processing) Amazon's SPECjbb2015 benchmarks show up to 22% heap savings. At scale, that translates directly into fewer replicas.
  • Generational Shenandoah (JEP 521). Shenandoah is now truly generational: young and old objects live separately. GC pauses become shorter and more predictable even on large heaps, with the collector touching the young generation more often and the rest less frequently.
  • AOT Method Profiling and ergonomics (JEP 515, 514). A continuation of Project Leyden. The JVM collects an execution profile, caches it, and applies it on the next launch — startup and warmup times shrink noticeably. For serverless functions and short CI jobs, this is critical.

Worth noting separately: the Vector API is in its tenth incubator in Java 25 — still no final release, as it's waiting on Valhalla. Not relevant for 90% of services, but teams doing ML inference on the JVM are keeping an eye on it.

When to migrate and when to wait

Java 25 is designed as a compatible upgrade, but production is production. A grounded checklist by starting version:

  1. Migrate now. Projects on Java 21 already using Virtual Threads. Gains from Scoped Values, structured concurrency, and generational Shenandoah are immediate — plus the Oracle deadline in September 2026.
  2. Migrate this quarter. Cloud-native services on Java 17 with a noticeable memory bill or slow pod startup. Compact Object Headers and AOT profiles pay for the migration through infrastructure savings alone.
  3. Plan for 2026–2027. Legacy on Java 11 and below. Here the migration is a project of its own: first to 17, then to 25, factoring in the removed 32-bit x86 port (JEP 503) and dropped APIs.
  4. Don't rush. Anything tied to custom agents, bytecode manipulation, older Kafka Streams versions, or Spring Boot before 3.x. Validate the entire chain, starting with CI and build tools.

Recent months confirm the spread: teams whose builds and observability are in order push the Java 21 → 25 migration to prod in 2–4 weeks. Those stuck on Java 8 or 11 still face a multi-month effort, and Java 25 alone doesn't shorten it.

Automated migration: OpenRewrite and the pre-rollout checklist

A manual upgrade from Java 21 to Java 25 is mostly bumping versions in pom.xml/build.gradle, but the small stuff always surfaces: removed APIs, changed JFR defaults, GC behavior. The de facto standard for automation is OpenRewrite recipes (org.openrewrite.recipe:rewrite-migrate-java). A single run walks the codebase, updates dependencies, fixes obvious incompatibilities, and leaves only the debatable spots for a human. On large monorepos, this saves weeks.

Before rolling Java 25 out to staging, three things are worth checking:

  • Build tooling: Maven, Gradle, plugins, annotation processors — compatibility with JDK 25.
  • APM and profilers: JFR in Java 25 gained cooperative sampling and CPU-time profiling — older integrations may not "see" the new events.
  • Container images: revisit jlink modules and base images, especially if you're on Alpine or distroless.

Framework support is a separate story. Spring Boot 3.5.5 and Spring Boot 4.0 are officially Java 25 ready (the latter on Spring Framework 7). Older versions will run, but without guarantees on benchmarks or observability. For Quarkus and Micronaut, check their release notes before migrating.

The bottom line

Java 25 is a rare release where the payoff for cloud services is visible without rewriting a line of code. Compact Object Headers and generational Shenandoah deliver memory savings and predictable pauses. Structured Concurrency and Scoped Values close the remaining debts of virtual threads. AOT profiles speed up startup where the JVM was traditionally considered weak. Add the expiring free updates for Java 21 in September 2026, and you get not a philosophical but a calendar-based argument for migration. If you're already on Java 21, plan the upgrade in the coming months. If you're on 17, Java 25 is a good reason to skip the interim releases and get eight years of support in one step.

← Back to Articles
Сloud Builders: Java Conf 2023
Page: Сloud Builders: Java Conf 2023