Reasons to move to Java 25

Java 25 is a long-term support (LTS) release, which makes it the natural target for teams that run on Java 8, 11, 17, or 21 today. Each LTS release receives years of updates and security fixes, so moving to Java 25 keeps your applications supported, secure, and able to use the latest performance and language improvements.

The question isn't if you should move to a newer LTS release, but when. The longer you wait, the more changes accumulate between your current version and the next supported one. There are clear benefits to moving to Java 25, and planning the move sooner rather than later reduces risk.

Since Java 8, every release has added features and enhancements. There are noticeable additions and modifications to the API, along with improvements to startup time, throughput, memory usage, scalability, and developer productivity. This article highlights the most impactful changes, with a focus on the Java versions delivered since the Java 11 LTS release: Java 17, Java 21, and Java 25.

Transition to Java 25

You can transition to Java 25 in a stepwise fashion. Code developed and built with an older JDK can usually run on Java 25 without recompilation. As with any major upgrade, watch for removed APIs, deprecated packages, use of internal APIs, and changes to default behavior, especially around garbage collection.

If you're coming from Java 8 or 11, start with the transition from Java 8 to Java 11 guide to address the largest set of breaking changes, and then continue forward to Java 17, 21, and 25. If you're already on Java 17 or 21, the move to Java 25 is typically smaller because the module system and most platform changes are already behind you.

Tip

GitHub Copilot app modernization can help you assess your application, plan the upgrade, and apply code changes automatically. It supports upgrades between Java versions 8, 11, 17, 21, and 25, builds and tests your project after each change, and helps remediate the behavior changes described later in this article.

Tip

JVM flags that were tuned for an older JDK can become deprecated, removed, or suboptimal on Java 25. Instead of carrying over stale settings, you can let the Azure Command Launcher for Java (jaz) apply JVM flags tailored to the JDK version and your container or virtual machine resources automatically. Replace the java command with jaz in your launch script or Dockerfile to benefit.

High-level changes since Java 11

This section doesn't enumerate every change made since Java 8. It highlights the changes that have the greatest impact on performance, scalability, diagnostics, productivity, and security. Most of these features arrived in the Java 17, 21, and 25 releases and are now part of the LTS baseline you get with Java 25. Each feature notes the Java release in which it was integrated.

Scalability with virtual threads

Virtual threads [1] are lightweight threads, managed by the JVM rather than the operating system, that dramatically reduce the cost of writing and running high-throughput concurrent applications. With virtual threads, you can write code in a simple thread-per-request style and still scale to large numbers of concurrent operations, which is ideal for I/O-bound microservices and request-response workloads.

Virtual threads were finalized in Java 21. Java 24 removed a key limitation by allowing virtual threads to synchronize without pinning their carrier threads [2], which improves scalability for code that uses synchronized blocks.

Structured concurrency and scoped values

Scoped values [3] provide a safe, efficient way to share immutable data within and across threads, including virtual threads. They're a modern alternative to thread-local variables that works well with the thread-per-task model.

Structured concurrency [4] treats groups of related concurrent tasks as a single unit of work, which simplifies error handling and cancellation. First previewed in Java 21, it continues to evolve and pairs naturally with virtual threads.

Language productivity

A series of language enhancements delivered across Java 14 through Java 25 make code more concise, safer, and easier to read:

  • Switch expressions [5] (Java 14) let switch return a value and use a cleaner arrow syntax.
  • Pattern matching for instanceof [6] (Java 16) removes the need for explicit casts after a type check.
  • Records [7] (Java 16) model immutable data as transparent carriers for a fixed set of values, reducing boilerplate.
  • Sealed classes [8] (Java 17) let you control which classes can extend or implement a type.
  • Text blocks [9] (Java 15) simplify multiline string literals.
  • Pattern matching for switch [10] (Java 21) and record patterns [11] (Java 21) let you express complex conditional logic and data destructuring clearly and safely.
  • Sequenced collections [12] (Java 21) add a uniform API for collections with a defined encounter order, including access to first and last elements.
  • Unnamed variables and patterns [13] (Java 22) let you mark unused variables and pattern components explicitly.
  • Module import declarations [14] (Java 25) let you import an entire module's exported packages with a single declaration.
  • Compact source files and instance main methods [15] (Java 25) reduce the ceremony required for small programs, which makes Java easier to learn and faster to prototype.
  • Flexible constructor bodies [16] (Java 25) let you run statements before calling super(...) or this(...), which improves validation and initialization.

Performance and startup

Several runtime changes improve throughput, memory footprint, and startup time:

  • Compact object headers [17] (Java 25) reduce the size of object headers on 64-bit platforms. This reduction lowers heap usage and can improve performance for allocation-heavy workloads.
  • Class data sharing (CDS) lowers startup time by memory-mapping archived classes at runtime. Default CDS archives [18] (Java 12) and dynamic CDS archives [19] (Java 13) make this feature easier to adopt without manual training runs.
  • Ahead-of-time class loading and linking [20] (Java 24), together with ahead-of-time command-line ergonomics [21] (Java 25) and ahead-of-time method profiling [22] (Java 25), shorten startup and warmup time by reusing work captured in an application archive. These features are part of Project Leyden's effort to make Java start faster.

These improvements are especially valuable for cloud-native and serverless workloads, where fast startup and a small footprint translate directly into better scaling and lower cost.

Garbage collection

Java 25 ships with mature, low-pause garbage collectors suitable for a wide range of workloads:

  • ZGC [23] (Java 15) and Shenandoah [24] (Java 15) are production-ready collectors designed for low pause times on large heaps.
  • G1GC remains the default collector and continues to receive improvements, including region pinning [25] (Java 22) for smoother interaction with native code.
  • Generational ZGC [26] (Java 21) improves efficiency by maintaining separate generations. As of Java 23, ZGC runs in generational mode by default [27], and the non-generational mode was removed in Java 24.
  • Generational Shenandoah [28] (Java 25) adds a generational mode to the Shenandoah collector for better throughput and resilience under memory pressure.

The JVM sets GC defaults for the average use case. Tune these defaults and other GC settings to optimize throughput or latency according to your application's requirements.

Tip

The default garbage collector and many JVM defaults differ across Java versions, so settings tuned for Java 8 or 11 might no longer be optimal on Java 25. The Azure Command Launcher for Java (jaz) reads the container's cgroup memory and CPU limits and applies JVM flags tailored to the JDK version and environment automatically.

Diagnostics and observability

Diagnostics improved across releases, both in the language and in Java Flight Recorder (JFR):

  • Helpful NullPointerExceptions [29] (Java 14) describe precisely which variable was null, which speeds up debugging.
  • JFR event streaming [30] (Java 14) lets tools consume profiling and diagnostic data continuously rather than from a dump file.
  • JFR CPU-time profiling [31] (Java 25) adds experimental CPU-time based method profiling on Linux.
  • JFR cooperative sampling [32] (Java 25) improves the stability of stack sampling.
  • JFR method timing and tracing [33] (Java 25) lets you time and trace specific methods without modifying application code.

Security and cryptography

Java 25 strengthens the platform's security posture, including preparation for a post-quantum world:

  • Edwards-Curve Digital Signature Algorithm (EdDSA) [34] (Java 15) adds a modern, high-performance signature scheme.
  • Key Encapsulation Mechanism API [35] (Java 21) provides a standard API for KEM algorithms.
  • Quantum-resistant cryptography adds standard implementations of the module-lattice-based key encapsulation mechanism (ML-KEM) [36] (Java 24) and digital signature algorithm (ML-DSA) [37] (Java 24).
  • Key Derivation Function API [38] (Java 25) provides a standard API for key derivation functions.

Native interoperability

The Foreign Function and Memory API [39] (Java 22) provides a safe, efficient, and pure-Java way to call native libraries and access native memory. It's a modern replacement for the Java Native Interface (JNI) that reduces boilerplate and improves safety.

Tooling and libraries

Several additions improve everyday development and reduce the need for third-party tools:

  • Packaging tool (jpackage) [40] (Java 16) creates native installers and packages for Java applications.
  • Simple Web Server [41] (Java 18) provides a minimal static-file HTTP server for prototyping and testing.
  • Enhanced pseudorandom number generators [42] (Java 17) add new interfaces and implementations for random number generation.

Behavior changes to plan for

Some changes since Java 11 alter default behavior, so review them before you upgrade:

  • Strongly encapsulate JDK internals [43] (Java 17) blocks reflective access to most internal APIs by default. Code or libraries that reach into sun.* or other internal packages might need updates.
  • UTF-8 by default [44] (Java 18) makes UTF-8 the default charset for standard Java APIs. Applications that relied on a platform-specific default charset might behave differently.
  • Deprecate finalization for removal [45] (Java 18) signals that finalize() will eventually be removed. Migrate to try-with-resources or java.lang.ref.Cleaner.
  • Prepare to disallow the dynamic loading of agents [46] (Java 21) warns when an agent is loaded into a running JVM. Some monitoring and instrumentation tools might need configuration changes.

Tip

GitHub Copilot app modernization can help you assess and remediate these behavior changes automatically when you upgrade.

Containers and the cloud

The JVM's container awareness, introduced for Docker and other container runtimes, continues to improve. The JVM reads CPU and memory constraints set by container control groups (cgroups) and sizes the heap and other resources accordingly. Combined with faster startup, a smaller footprint, and virtual threads, Java 25 is well suited to containerized and serverless deployments on Azure.

If you run Java workloads on Azure containers or virtual machines, the Azure Command Launcher for Java (jaz) can apply cloud-optimized, version-appropriate JVM defaults for you.

Next steps

References

[1] Oracle Corporation, "JEP 444: Virtual Threads." (Online). Available: https://openjdk.org/jeps/444.

[2] Oracle Corporation, "JEP 491: Synchronize Virtual Threads without Pinning." (Online). Available: https://openjdk.org/jeps/491.

[3] Oracle Corporation, "JEP 506: Scoped Values." (Online). Available: https://openjdk.org/jeps/506.

[4] Oracle Corporation, "JEP 453: Structured Concurrency (Preview)." (Online). Available: https://openjdk.org/jeps/453.

[5] Oracle Corporation, "JEP 361: Switch Expressions." (Online). Available: https://openjdk.org/jeps/361.

[6] Oracle Corporation, "JEP 394: Pattern Matching for instanceof." (Online). Available: https://openjdk.org/jeps/394.

[7] Oracle Corporation, "JEP 395: Records." (Online). Available: https://openjdk.org/jeps/395.

[8] Oracle Corporation, "JEP 409: Sealed Classes." (Online). Available: https://openjdk.org/jeps/409.

[9] Oracle Corporation, "JEP 378: Text Blocks." (Online). Available: https://openjdk.org/jeps/378.

[10] Oracle Corporation, "JEP 441: Pattern Matching for switch." (Online). Available: https://openjdk.org/jeps/441.

[11] Oracle Corporation, "JEP 440: Record Patterns." (Online). Available: https://openjdk.org/jeps/440.

[12] Oracle Corporation, "JEP 431: Sequenced Collections." (Online). Available: https://openjdk.org/jeps/431.

[13] Oracle Corporation, "JEP 456: Unnamed Variables & Patterns." (Online). Available: https://openjdk.org/jeps/456.

[14] Oracle Corporation, "JEP 511: Module Import Declarations." (Online). Available: https://openjdk.org/jeps/511.

[15] Oracle Corporation, "JEP 512: Compact Source Files and Instance Main Methods." (Online). Available: https://openjdk.org/jeps/512.

[16] Oracle Corporation, "JEP 513: Flexible Constructor Bodies." (Online). Available: https://openjdk.org/jeps/513.

[17] Oracle Corporation, "JEP 519: Compact Object Headers." (Online). Available: https://openjdk.org/jeps/519.

[18] Oracle Corporation, "JEP 341: Default CDS Archives." (Online). Available: https://openjdk.org/jeps/341.

[19] Oracle Corporation, "JEP 350: Dynamic CDS Archives." (Online). Available: https://openjdk.org/jeps/350.

[20] Oracle Corporation, "JEP 483: Ahead-of-Time Class Loading & Linking." (Online). Available: https://openjdk.org/jeps/483.

[21] Oracle Corporation, "JEP 514: Ahead-of-Time Command-Line Ergonomics." (Online). Available: https://openjdk.org/jeps/514.

[22] Oracle Corporation, "JEP 515: Ahead-of-Time Method Profiling." (Online). Available: https://openjdk.org/jeps/515.

[23] Oracle Corporation, "JEP 377: ZGC: A Scalable Low-Latency Garbage Collector." (Online). Available: https://openjdk.org/jeps/377.

[24] Oracle Corporation, "JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector." (Online). Available: https://openjdk.org/jeps/379.

[25] Oracle Corporation, "JEP 423: Region Pinning for G1." (Online). Available: https://openjdk.org/jeps/423.

[26] Oracle Corporation, "JEP 439: Generational ZGC." (Online). Available: https://openjdk.org/jeps/439.

[27] Oracle Corporation, "JEP 474: ZGC: Generational Mode by Default." (Online). Available: https://openjdk.org/jeps/474.

[28] Oracle Corporation, "JEP 521: Generational Shenandoah." (Online). Available: https://openjdk.org/jeps/521.

[29] Oracle Corporation, "JEP 358: Helpful NullPointerExceptions." (Online). Available: https://openjdk.org/jeps/358.

[30] Oracle Corporation, "JEP 349: JFR Event Streaming." (Online). Available: https://openjdk.org/jeps/349.

[31] Oracle Corporation, "JEP 509: JFR CPU-Time Profiling (Experimental)." (Online). Available: https://openjdk.org/jeps/509.

[32] Oracle Corporation, "JEP 518: JFR Cooperative Sampling." (Online). Available: https://openjdk.org/jeps/518.

[33] Oracle Corporation, "JEP 520: JFR Method Timing & Tracing." (Online). Available: https://openjdk.org/jeps/520.

[34] Oracle Corporation, "JEP 339: Edwards-Curve Digital Signature Algorithm (EdDSA)." (Online). Available: https://openjdk.org/jeps/339.

[35] Oracle Corporation, "JEP 452: Key Encapsulation Mechanism API." (Online). Available: https://openjdk.org/jeps/452.

[36] Oracle Corporation, "JEP 496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism." (Online). Available: https://openjdk.org/jeps/496.

[37] Oracle Corporation, "JEP 497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm." (Online). Available: https://openjdk.org/jeps/497.

[38] Oracle Corporation, "JEP 510: Key Derivation Function API." (Online). Available: https://openjdk.org/jeps/510.

[39] Oracle Corporation, "JEP 454: Foreign Function & Memory API." (Online). Available: https://openjdk.org/jeps/454.

[40] Oracle Corporation, "JEP 392: Packaging Tool." (Online). Available: https://openjdk.org/jeps/392.

[41] Oracle Corporation, "JEP 408: Simple Web Server." (Online). Available: https://openjdk.org/jeps/408.

[42] Oracle Corporation, "JEP 356: Enhanced Pseudo-Random Number Generators." (Online). Available: https://openjdk.org/jeps/356.

[43] Oracle Corporation, "JEP 403: Strongly Encapsulate JDK Internals." (Online). Available: https://openjdk.org/jeps/403.

[44] Oracle Corporation, "JEP 400: UTF-8 by Default." (Online). Available: https://openjdk.org/jeps/400.

[45] Oracle Corporation, "JEP 421: Deprecate Finalization for Removal." (Online). Available: https://openjdk.org/jeps/421.

[46] Oracle Corporation, "JEP 451: Prepare to Disallow the Dynamic Loading of Agents." (Online). Available: https://openjdk.org/jeps/451.