A new LTS version has always been the big news in the Java world, and JDK 21 is no exception. It was moved to Rampdown Phase One on June 16 meaning that the feature set has been frozen. An extensive set of 15 JEPs includes new, enhanced, and finalized functionality. Let’s take a dive into the upcoming LTS release — it might be just the version you will stick to for the years ahead!
Table of Contents
Novelties
JEP 430: String Templates (Preview)
String templates are a preview language feature and API aimed at facilitating the expression of strings that contain values computed at run time. The existing Java mechanisms of concatenating literal text and expressions produce hard-to-read code or are associated with verbosity. Other languages utilize string interpolation, which allows for conciseness but brings potential security risks.
Template expressions help to achieve clarity of interpolation without introducing security vulnerabilities.
Take a look at the code snippet with a template expression on the second line:
String name = "Joan";
String info = STR."My name is \{name}";
assert info.equals("My name is Joan"); // true
The expression consists of a template processor STR
, a dot, and a template with an embedded expression \{name}
. Embedded expressions can be strings, perform arithmetic operations, invoke methods, access fields, and even spread over multiple lines.
Template processors can use only the values in the embedded expressions, and execute at run time only. In addition, it is impossible to use the templates without the template processor responsible for safe interpolation and validation of a result, which increases safety of operations.
JEP 431: Sequenced Collections
Sequenced collections introduces collections with a defined encounter order and uniform APIs for accessing the first and last elements, and processing the elements in forward and reverse order.
Three new interfaces — sequenced collections, sets, and maps — will be retrofitted into the existing collections type hierarchy. All three interfaces have new methods facilitating the development process:
- A sequenced collection has a
reversed()
method to view the collection in reversed order, process the elements in both directions, and perform all the usual iteration operations such asforEach()
,stream()
, etc. - A sequenced set includes
addFirst(E)
andaddLast(E)
methods that can move the elements in the appropriate position if it is already present in the set. - A sequenced map has the
put*(K, V)
methods whose functionality is similar toadd*(E)
methods of sequenced sets.
JEP 443: Unnamed Patterns and Variables (Preview)
The unnamed patterns and variables will improve the readability and maintainability of code by
- Eliding the unnecessary type and name of a record component in pattern matching and
- Identifying variables that must be declared but will not be used.
Both are denoted by the underscore character _ .
Unnamed patterns enable the developers to omit the components in record patterns that are not used, for instance:
... instanceof Point(int x, _)
case Point(int x, _)
Unnamed variables substitute the names of variables, which are not used (for example, in try-with-resources
or try-catch
blocks), e.g.:
int _ = q.remove();
... } catch (NumberFormatException _) { ...
(int x, int _) -> x + x
JEP 445: Unnamed Classes and Instance Main Methods (Preview)
Students embarking on a Java development journey may find some enterprise-level features too difficult. The new feature is aimed at giving them the opportunity to write single-class programs and gradually expand them as their knowledge grows. It will also be useful for experienced developers who want to write simple, concise applications without programming-in-the-large composition of program components (i.e., when enterprise-level features interact with each other through well-defined protocols, but hide internal implementation details).
For instance, the basic HelloWorld program contains several features, which are hard to comprehend, but unnecessary for novices:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
It can be simplified to:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
This program can be made more complex with time as students learn necessary concepts. But at the same time, there's no need to introduce a simplified Java dialect for educational purposes.
JEP 451: Prepare to Disallow the Dynamic Loading of Agents
In JDK 21, the users will receive warnings when agents are loaded dynamically into a running JVM. JEP 451 lays ground for a future release that disallows the dynamic loading of agents by default in line with the ongoing process of enhancing Java integrity.
Agents are components that can alter the code when the application is running. They are commonly used by serviceability tools such as profilers and troubleshooting instruments, but the developer must grant approval to alter the application. However, some libraries that use agents can bypass this requirement and attach to the running JVM silently, thus increasing the security risks.
The proposal is to make the user explicitly allow the dynamic loading with the -XX:+EnableDynamicAgentLoading
option on the command line. Luckily, most serviceability tools do not use dynamic agent loading and therefore, will not be affected. As for the libraries, they must load the agent at startup with the -javaagent/-agentlib options: the maintainers are encouraged to update their documentation with the explanation of how to load agents at startup.
JEP 452: Key Encapsulation Mechanism API
The Key Encapsulation Mechanism (KEM) API introduces an encryption technique for securing symmetric keys with asymmetric (public key) cryptography. KEM uses public key properties to derive a related symmetric key without padding.
Right now, Java doesn’t have a standard KEM API. However, it is an important modern technique for defending against cyberattacks and will likely be part of the next generation of standard public key cryptography algorithms.
Finalized features
These features were introduced in previous releases and after a series of improvements and follow-up changes have taken a final form in this LTS version.
JEP 440: Record Patterns
Record patterns, which are used to deconstruct record values to improve pattern matching, were first introduced in JDK 19. JEP 440 finalizes the feature with several enhancements based on the feedback. The most significant change is the removal of support for record patterns appearing in the header of an enhanced for
statement.
JEP 441: Pattern Matching for switch
Pattern matching for switch
expressions and statements was proposed in JDK 17 and refined in the following releases. The aim of the functionality is to enhance the expressiveness, applicability, and safety of switch
statements. With pattern matching for switch
, developers can test the expressions against specific patterns, thus making complex data queries more concise and reliable. The finalized feature includes the following improvements:
- The removal of parenthesized patterns;
- Allowing for qualified enum constants as case constants in
switch
.
JEP 444: Virtual Threads
Virtual threads enhance the concurrent programming in Java by providing a mechanism to create thousands of lightweight threads depending on the tasks at hand, which can be monitored, managed, and debugged like the usual platform threads. You can read more about this functionality in the dedicated article.
Virtual threads were included as a preview API in JDK 19. JEP 444 finalizes the feature and includes a few improvements based on the feedback:
- Always support thread-local variables belonging to the ThreadLocal API that enables the developers to store data accessible for a specific thread only;
- Virtual threads created directly with the Thread.Builder API are now monitored during their lifetime by default. They can also be observed via the new thread dump, which will group plentiful virtual threads in a meaningful way.
Improved features
JEP 439: Generational ZGC
ZGC is a scalable low-latency garbage collector that has consistently low pause times (measured in microseconds) regardless of the heap size. However, the current non-generational ZGC stores young and old objects together and has to collect all objects every time it operates. As most young objects die young, and old objects tend to stick around, collecting young objects requires fewer resources and yields more memory. Therefore, a Generational ZGC will maintain young and old objects separately and collect young objects more frequently, thus reducing the GC CPU overhead and heap memory overhead.
JEP 442: Foreign Function & Memory API (Third Preview)
Foreign function & memory API enables Java applications to safely interact with code and data outside of the Java runtime. The FFM API is aimed at replacing the Java Native Interface with a more reliable, pure-Java development model.
This is the third preview of the FFM API with the following amendments:
- Centralized management of the lifecycle of native segments through the Arena interface;
- Enhanced layout paths with a new element to dereference address layouts;
- A new linker option to optimize calls to short-lived functions that will not upcall to Java;
- A new fallback native linker implementation, based on libffi, to facilitate porting;
- Removed VaList class.
JEP 446: Scoped Values (Preview)
Scoped values enable the developers to share immutable data within and across threads with the aim of more reliable and manageable data management in concurrent applications.
Scoped values should be preferred to thread-local variables, because, unlike them, scoped values are immutable and are associated with smaller footprint and complexity.
This feature was incubated in JDK 20 and is now a preview API.
JEP 448: Vector API (Sixth Incubator)
Vector API increases the performance of vector computations that compile reliably at run time to optimal vector instructions. The feature was first introduced in JDK 16. You can find out more about Vector API performance in an article dedicated to Java 17 features.
This is a sixth incubator with the following notable enhancements apart from bug fixes:
- Addition of the exclusive or (xor) operation to vector masks.
- Improved performance of vector shuffles, especially when used to rearrange the elements of a vector and when converting between vectors.
JEP 453: Structured Concurrency (Preview)
Structured concurrency enables the reliable coordination of virtual threads and improves observability and maintainability of concurrent code.
The feature was included into JDK 19 as an incubating API and reincubated in subsequent releases. This is a preview API with one notable change: the StructuredTaskScope::fork(...)
method returns a [Subtask] instead of a Future as before. The Future is more useful when multiple tasks are treated as individual tasks, and not as a single unit of work (which is the goal of structured concurrency). The Future involves calling a get()
method, which blocks until a result is available. This is counterproductive in the case of StructuredTaskScope, which will now use a resultNow()
that never blocks.
Deprecated functionality
JEP 449: Deprecate the Windows 32-bit x86 Port for Removal
The last Windows OS that supports 32-bit operation (Windows 10) will reach end of life in October 2025. At the same time, the usage of virtual threads on Windows x86-32 doesn’t bring the expected benefits. Therefore, the Windows 32-bit x86 port becomes redundant and will be removed in a future release.
To upgrade or stay put?
Early-access builds are already available. If you are going to upgrade to the new LTS version, you can test the new functionality and start planning the migration strategy. BellSoft will support JDK 21 until March 2032, so consider making this version a solid ground for your project for the coming years.
But at the same time, we understand how complicated and time-consuming the migration process might be, and the further you are from the current version, the more issues you will have to deal with upon upgrading. What if you could bring the performance of newer versions to your JDK 11-based enterprise application without switching the Java version? BellSoft has the solution for you – no significant adjustment, no code rewriting, no compatibility problems, but instant performance boost!
Meet Liberica JDK Performance Edition, which couples JVM 17 and JDK 11 and improves the startup, throughput, and latency of application even at the default settings.
Click on the button below to learn more about its capabilities and features.