JDK 21: The new features in Java 21

Java 21 enters the home stretch with 15 finalized features including virtual threads, a generational ZGC, and a key encapsulation mechanism API.

Java 21 is brewing
jazz3311 / Shutterstock

The feature set for Java Development Kit (JDK) 21, due September 19 as the next long-term support (LTS) release of Oracle’s standard Java implementation, is officially frozen. Java 21 will have 15 features, as a previously proposed 16th feature, the experimental Shenandoah garbage collector, was dropped last month.

The 15 features that made the final cut range from previews of string templates and structured concurrency to virtual threads to a key encapsulation mechanism (KEM) API. JDK 21 reached a second rampdown phase July 20, following an initial rampdown phase that began June 8. Release candidates are due August 10 and August 24.

Early-access binaries under the GPL are available at jdk.java.net. Oracle publishes new releases of standard Java every six months, with the most recent, JDK 20, having arrived March 21. The specific proposals for JDK 21 include:

  • Structured concurrency, in a preview stage, simplifies concurrent programming via an API for structured concurrency, treating groups of related tasks running in different threads as a single unit of work. This streamlines error handling and cancellation, improving reliability and enhancing observability. Structured concurrency previously was incubated in JDK 20 and JDK 19, released in March and September 2022 respectively; it is to be featured as a preview API in the java.util.concurrent package. The only significant change this time around is that the StructuredTaskScope::Fork(…) method returns a [Subtask] rather than a Future. Goals of structured concurrency include promoting a style of concurrent programming that can eliminate common risks arising from cancellation and shutdown, such as thread leaks and cancellation delays, along with improving the observability of concurrent code.
  • Scoped values, also in preview, will enable the sharing of immutable data within and across threads. They are preferred to thread-local variables, particularly when using large numbers of virtual threads. Thread-local variables have design flaws including unconstrained mutability, unbounded lifetime, and expensive inheritance. A scoped value allows data to be safely shared between components in a large program without resorting to method arguments. This proposal was incubated in JDK 20. Goals of the plan include ease of use, comprehensibility, robustness, and performance.
  • A proposal to prepare to disallow the dynamic loading of agents calls for issuing warnings when agents are loaded dynamically into a running JVM. These warnings are intended to prepare for a future release that disallows the dynamic loading of agents by default, in order to improve integrity by default. Other goals of the proposal include reassessing the balance between serviceability, which involves ad hoc changes to running code, and integrity, which assumes running code is not arbitrarily changed, and ensuring that the majority of tools, which do not need to load agents dynamically, are unaffected. The plan also calls for aligning the ability to load agents dynamically with other so-called “superpower” capabilities such as deep reflection. An agent is a component that can alter application code while the application is running; these were introduced by the Java Platform Profiling Architecture in JDK 5 in 2004 as a way for tools (notably profilers) to instrument classes. While agents were designed with benign instrumentation in mind, advanced developers found use cases, such as aspect-oriented programming, that change application behavior in arbitrary ways. There also is nothing to stop an agent from altering code outside the application, such as code in the JDK itself. JDK 5 required agents to be specified on the command line, to ensure that the owner of an application approved the use of agents. With JDK 21, plans call for requiring dynamic loading of agents to be approved by the application owner, just as has been required with the startup-time loading of agents. This change will move the Java platform closer to integrity by default.
  • An API for key encapsulation mechanisms, an encryption technique for securing symmetric keys via public cryptography. One goal of the proposal is to enable applications to use KEM algorithms such as the RSA Key Encapsulation Mechanism (RSA-KEM), the Elliptic Curve Integrated Encryption Scheme (ECIES), and candidate algorithms for the National Institute of Standards and Technology (NIST) Post-Quantum Cryptography standardization process. Another goal is to enable the use of KEMs in higher-level protocols such as Transport Layer Security (TLS) and in cryptographic schemes such as Hybrid Public Key Encryption (HPKE). Security providers would be able to implement KEM algorithms in either Java code or native code, and include an implementation of the Diffie-Hellman KEM (DHKEM) defined in RFC 9180.
  • Deprecation of the Windows 32-bit x86 port for removal, with the goal to remove the port in a future release. The proposal is intended to update the build system to issue an error message when an attempt is made to configure a build for Windows 32-bit x86. The message will be suppressible via a new configuration option. Also, the plan is to mark the port, and related port-specific features, as deprecated for removal in relevant documentation. The proposal notes that Windows 10, the last Windows operating system to support 32-bit operation, reaches end of life in October 2025.
  • A preview of unnamed classes and instance main methods, to evolve the Java language so that students will be able write their first Java programs without needing to understand language features designed for large programs. Far from using a separate dialect of Java, students could write streamlined declarations for single-class programs and then seamlessly expand programs to use more advanced features as their skills grow. The proposal not only would offer a smooth onramp to Java but also reduce the ceremony involved in writing simple Java programs such as scripts and command-line utilities.
  • A preview of unnamed patterns and variables. Unnamed patterns match a record component without stating the component’s name or type, while unnamed variables can be initialized but not used. Both are denoted by an underscore character, _. This proposal is intended to improve the readability of record patterns by eliding unnecessary nested patterns, and to improve maintainability of all code by identifying variables that must be declared but will not be used.
  • Generational ZGC is intended to improve application performance by extending ZGC to maintain separate generations for young and old objects. Young objects tend to die young; maintaining separate generations will allow ZGC to collect young objects more frequently. Applications running with generational ZGC should see the following benefits: lower risks of allocation stalls, lower required heap memory overhead, and lower garbage collection CPU overhead. These benefits should be achievable without significant throughput reduction compared to non-generational ZGC.
  • Record patterns, previewed in both JDK 19 and JDK 20, would deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing. Goals of the proposal include extending pattern matching to destructure instances of record classes and adding nested patterns, enabling more composable data queries. This feature has co-evolved with pattern matching for switch expressions and statements (see below). The record patterns proposal in the current JEP (JDK Enhancement Proposal) would finalize the feature with further refinements based on continued experience and feedback. Apart from minor editorial changes, the main change since the second preview is to remove support for record patterns appearing in the header of an enhanced for statement. The feature may be re-proposed in a future JEP.
  • Pattern matching for switch enables a switch expression or statement to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed safely and concisely. This feature originally was proposed in JDK 17 and subsequently refined in JDK 18, JDK 19, and JDK 20. It would be finalized in JDK 21 with further refinements based upon feedback and experience. Main changes from previous JEPs are the removal of parenthesized patterns and allowing qualified enum constants such as case constants with switch expressions and statements. Goals include expanding the expressiveness and applicability of switch expressions and statements by allowing patterns to appear in case labels, allowing the historical null-hostility of switch to be relaxed when desired, and increasing the safety of switch statements by requiring that pattern switch statements cover all potential input values. Another goal is ensuring that existing switch expressions and statements continue to compile with no changes and execute with identical semantics.
  • A sixth incubator of a vector API. This API expresses vector computations that reliably compile to optimal vector instructions on supported CPU architectures, achieving performance superior to equivalent scalar computations. The vector API previously was incubated in JDK 16 through JDK 20. This latest incarnation includes performance enhancements and bug fixes. Goals of the proposal include being clear and concise, being platform agnostic, and offering reliable runtime compilation and performance on x64 and AArch64 architectures. Other goals include graceful degradation when a vector computation cannot be fully expressed as a sequence of vector instructions.
  • A third preview of a foreign function and memory API, which enables Java programs to interoperate with code and data outside the Java runtime. By efficiently invoking foreign functions and safely accessing foreign memory, this API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI (Java Native Interface). The API previously was previewed in JDK 20 and JDK 19. Refinements in the JDK 21 preview include enhanced layout paths with a new element to dereference address layouts, centralized management of the lifetimes of native segments in the Arena interface, a fallback native linker implementation, and removal of the VaList. Goals of the proposal include ease of use, performance, generality, and safety. It is not a goal to either reimplement JNI on top of this API or change JNI in any way.
  • Virtual threads are lightweight threads that promise to dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications. Goals of the plan include enabling server applications written in the simple thread-per-request style to scale with near-optimal hardware utilization, enabling existing code that uses the lang.Thread API to adopt virtual threads with minimal changes, and enabling easy debugging and profiling of virtual threads with current JDK tools. Previously previewed in both JDK 20 and JDK 19, virtual threads will be finalized in JDK 21. With JDK 21, virtual threads now support thread-local variables all of the time, and make it impossible to create virtual threads that do not have these variables. Guaranteed support for thread-local variables ensures that more existing libraries can be used unchanged with virtual threads and assists with migrating task-oriented code to use virtual threads.
  • The sequenced collections proposal introduces interfaces to represent collections with a defined encounter order. Each collection has well-defined first and second elements and so forth, to the last element. Uniform APIs are provided for accepting first and last elements and processing elements in reverse order. Motivating the proposal is a situation in which Java’s collections framework lacks a collection type to represent a sequence of elements with a defined encounter order. It also lacks a uniform set of operations that apply across these collections. These gaps have been a problem and a source of complaints. The proposal calls for defining interfaces for sequencing collections, sets, and maps, and retrofitting these interfaces into the existing collections type hierarchy. All of these new methods have default implementations.
  • String templates, a preview feature in JDK 21, complement Java’s existing string literals and text blocks by coupling literal text with embedded expressions and processors to produce specialized results. This language feature and API is intended to simplify the writing of Java programs by making it easy to express strings that include values computed at runtime. It promises to enhance readability of expressions, improve program security, retain flexibility, and simplify the use of APIs that accept strings written in non-Java languages. Enabling development of non-string expressions derived from combining literal text and embedded expressions also is a goal.

Separate from these JDK Enhancement Proposals, JDK 21 is slated to usher in a change to the way the JDK assigns names to network interfaces on Windows, according to the Java team at Oracle. Maintainers of applications that do network multicasting or that use the java.net.NetworkInterface API are advised to note the change.

The JDK historically synthesized names for network interfaces on Windows. This has been changed to use names assigned by the Windows OS. The change may impact code that does a lookup of network interfaces using the NetworkInterface.GetbyName(String name) method. Another possible feature for JDK 21 is an experimental compact object headers capability, which is designed to reduce the size of object headers in the JVM to cut heap size. JDK 21 also is set to include key changes in JDK Flight Recorder, including making analysis of flight recordings easier from the command line.

As a long-term support release, JDK 21 would get five years of premier support and extended support until September 2031. The current LTS release is JDK 17, published in September 2021. Non-LTS releases, such as JDK 20 and JDK 19, receive only six months of premier support and no extended support. LTS releases arrive every two years.

Copyright © 2023 IDG Communications, Inc.

InfoWorld Technology of the Year Awards 2023. Now open for entries!