Java 25 was released on September 16, 2025, and most vendors treat it as a long-term support release (OpenJDK: JDK 25). For enterprise teams that is the signal to plan the next platform upgrade: LTS versions are the ones you run in production for years. This article focuses on what actually changes for backend services — memory footprint, startup and warm-up, concurrency, language ergonomics — and gives a practical upgrade path from Java 17 or 21, including what to check in Spring Boot applications.

Why upgrade at all

If you are on Java 17, you skipped two LTS cycles of improvements; if you are on 21, the jump is smaller but still meaningful. Three reasons we hear most from clients:

  • Lower infrastructure cost. Memory is the main driver of container and VM sizing, and Java 25 makes compact object headers a production feature.
  • Faster startup and warm-up. Important for autoscaling, serverless and frequent deployments in Kubernetes.
  • Support timelines. Frameworks move their baselines forward. Spring Boot 4 keeps Java 17 as the minimum but adds first-class Java 25 support (Spring blog, 2025), and libraries increasingly assume newer JDKs.

Java 25 at a glance

JDK 25 delivers 18 JEPs. The final (non-preview) features most relevant to backend teams:

JEP Feature Why it matters
519 Compact Object Headers Smaller objects, less heap, fewer GCs
514 Ahead-of-Time Command-Line Ergonomics Simpler creation of AOT caches for faster startup
515 Ahead-of-Time Method Profiling Faster warm-up using profiles from a training run
521 Generational Shenandoah Low-pause GC with generational efficiency
506 Scoped Values Safer, cheaper alternative to ThreadLocal, ideal with virtual threads
513 Flexible Constructor Bodies Validation and computation before super(...)
511 Module Import Declarations import module java.base; and similar
512 Compact Source Files and Instance Main Methods Scripts and small tools without boilerplate
510 Key Derivation Function API Standard API for KDFs such as HKDF

Preview and incubator features — structured concurrency (fifth preview), stable values, primitive types in patterns, the Vector API — are worth experimenting with, but we don't put preview APIs into production services.

Memory: compact object headers

Every Java object carries a header. JEP 519 turns compact object headers, introduced experimentally in JDK 24, into a product feature. The results quoted in the JEP are significant: in one setting the SPECjbb2015 benchmark used 22% less heap and 8% less CPU, and in another the number of garbage collections dropped by 15% (JEP 519). The JEP also notes the layout has been used by hundreds of services in production at Amazon.

It is not on by default. Enable it explicitly and measure:

java -XX:+UseCompactObjectHeaders -jar app.jar

For object-heavy services — caches, DTO-heavy APIs, event processing — this is one of the cheapest optimizations available: no code changes, potentially smaller pods.

Startup and warm-up: the AOT cache

Project Leyden's ahead-of-time cache stores the results of class loading and linking, and since JDK 25 also method profiles, from a training run. The next start skips that work and the JIT can optimize immediately (JEP 514; JEP 515). JDK 25 reduced creating the cache to a single step:

# Training run: exercise the app, then exit; the JVM writes the cache
java -XX:AOTCacheOutput=app.aot -jar app.jar

# Production runs use the cache
java -XX:AOTCache=app.aot -jar app.jar

Two practical notes: the training run should exercise realistic code paths (a startup smoke test or a short load test), and the one-step workflow needs about twice the configured heap while it creates the cache, as the JEP warns. Unlike GraalVM native images, the AOT cache keeps the full JVM, JIT and dynamic features; we compare the approaches in GraalVM native image for Spring Boot.

Garbage collection: generational Shenandoah

Shenandoah's generational mode became a product feature in JDK 25 (JEP 521). It combines Shenandoah's very short pauses with the efficiency of collecting young objects separately. G1 remains the default and the right choice for most services; consider generational Shenandoah or ZGC for latency-sensitive services with large heaps, and decide with load tests, not benchmarks from blogs.

Concurrency: scoped values

Scoped values let a method share immutable data with its callees and child threads, with a clear lifetime (JEP 506). They are cheaper and safer than ThreadLocal, especially with millions of virtual threads, where thread-locals multiply memory use.

private static final ScopedValue<RequestContext> CONTEXT = ScopedValue.newInstance();

void handle(Request request) {
    ScopedValue.where(CONTEXT, RequestContext.from(request))
               .run(() -> orderService.process(request));
}

// anywhere down the call stack, without passing parameters
RequestContext ctx = CONTEXT.get();

Virtual threads themselves arrived in Java 21; JDK 24 removed most cases of pinning in synchronized blocks, which makes them far more practical. We cover production use in Virtual threads in Spring Boot.

Language ergonomics

Flexible constructor bodies allow statements before super(...) or this(...), as long as they don't use the object under construction (JEP 513). Validation finally reads naturally:

public class Money extends Amount {
    public Money(BigDecimal value, Currency currency) {
        if (value.signum() < 0) throw new IllegalArgumentException("Negative amount");
        Objects.requireNonNull(currency);
        super(value.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_EVEN));
        this.currency = currency;
    }
}

Module import declarations (import module java.sql;) and compact source files with instance main methods make small tools and scripts pleasant to write in Java without a build setup (JEP 511; JEP 512).

An upgrade plan from Java 17 or 21

  1. Inventory. List services, their JDK, framework versions and build tools. Check that Gradle, Maven plugins, Lombok, Mockito, bytecode libraries and agents (APM, profilers) support Java 25.
  2. Upgrade frameworks first. Move to Spring Boot 3.5 or 4.x, Hibernate and other dependencies that officially support the target JDK. See Spring Boot + Kotlin in 2026 for the Spring Boot 3.5 → 4 path.
  3. Build with the new JDK, run tests, fix warnings. Typical issues between 17 and 25: removed or encapsulated internal APIs, stricter warnings about dynamic agent loading, and older bytecode-manipulation libraries.
  4. Run in staging under load. Compare heap, CPU, GC pauses, startup and p99 latency with the old version.
  5. Try the free wins. Enable compact object headers and an AOT cache on one service and measure.
  6. Roll out gradually, service by service, with the old image ready for rollback.
  7. Update base images and CI so new services start on Java 25 by default.

Upgrade issues we meet most often

Issue Typical symptom Fix
Old bytecode libraries (ASM, Byte Buddy, cglib) Errors about unsupported class file version Upgrade the library or the framework that bundles it
Lombok or annotation processors Compilation failures Upgrade to a version that supports the new JDK
Dynamic agent loading Warnings at startup from APM or mocking tools Load agents with -javaagent explicitly
Removed internal APIs IllegalAccessError, missing classes Replace with public APIs; avoid --add-opens as a permanent fix
Container memory settings Different heap sizing than expected Set -XX:MaxRAMPercentage explicitly and re-measure

FAQ

Is Java 25 really LTS? Most vendors, including Oracle, Eclipse Adoptium, Amazon Corretto and Azul, provide long-term support for it. Support length depends on the vendor.

Should we skip Java 21 and go straight from 17 to 25? Yes, if your frameworks support it. The migration effort from 17 to 25 is not much larger than to 21, and you get more years of support.

Does Java 25 make GraalVM native image unnecessary? Not entirely. The AOT cache improves startup a lot while keeping the full JVM; native images still start faster and use less memory but restrict dynamic features.

Do compact object headers break anything? They are transparent to Java code. Test native libraries and tools that inspect object layout, and measure before enabling everywhere.

Can we run Java 25 in production with Spring Boot 3? Spring Boot 3.5 supports Java up to 25 according to its support matrix, but upgrading to Spring Boot 4 gives first-class Java 25 support and a longer open-source support window.

Sources

  1. OpenJDK. JDK 25 project page.
  2. OpenJDK. JEP 519: Compact Object Headers.
  3. OpenJDK. JEP 514: AOT Command-Line Ergonomics and JEP 515: AOT Method Profiling.
  4. OpenJDK. JEP 521: Generational Shenandoah.
  5. OpenJDK. JEP 506: Scoped Values, JEP 513: Flexible Constructor Bodies, JEP 511: Module Import Declarations, JEP 512: Compact Source Files.
  6. Spring (2025). Spring Boot 4.0.0 available now.