How to better understand what JDK 19 brings? JVM Weekly 14
Today there could only be one topic - the release of JDK 19! I dedicated an entire edition to it, going through the various JEPs that make up this release and bring some additional context to each.
Well, there we have it! As scheduled, JDK 19 has been released. This release may seem small, but don't let the appearance of only one stable feature fool you - for we are dealing with one of the most important new JDKs in years. I'm inclined to say that even since JDK 9 with its module system. For the first time, we have the opportunity to use Preview versions of projects that are the future of the JVM.
Before I get to my summary of the various JEPs that went into this release - if you want to see the official announcements, Oracle has prepared as many as three. You can choose from:
release note from Oracle, summarizing the number of changes and people involved in creating JDK 19
podcast, whose guests are Ron Pressler and Brian Goetz
As the official part is behind us, now a few pennies from me. I decided to go through each JEP and elaborate a bit about each one.
422: Linux/RISC-V Port
Let's start with the one and only stable change coming in JDK 19 - well, Java is arriving to the brand new platform. This time it's Linux, but not that ordinary Linux - we are talking about Linux powered by RISC-V CPUs.
RISC-V - an alternative to ARM - is an open standard created by Americans at the University of Berkley. RISC-V is not an off-the-shelf processor but an instruction set architecture (ISA), trying to provide a strictly "reduced instruction set computer" - RISC. To quote Wikipedia:
Unlike most ISAs, RISC-V can be freely used for any purpose, allowing anyone to design, manufacture and sell RISC-V chips and software. Although it is not the first open ISA architecture, it is significant because it was designed for modern computerized devices such as massive cloud computing, high-end cell phones and the smallest embedded systems.
And that's why there's been a lot of buzz about this architecture lately. While it is not Chinese concept, the Chinese have begun to invest heavily in it. In addition to its undoubted advantages on the technical side (RISC-V programmers are currently looking for min. Apple), the architecture's popularity is a result of the recent political turmoil. Indeed, as a real open standard, it appears as a kind of safe haven for all those who do not want to depend on ARM's vision and the US sphere of influence. Hence the interest from China, Russia, and... the European Union. Last year, the EU announced the creation of the RISC-V-based EPAC (European Processor Accelerator) chip, which is expected to be a milestone in gaining a foothold in the EU's processor manufacturing market.
I myself am watching RISC-V with interest and hope that it will live up to the hopes placed in it. As far as the processor market is concerned, I don't think there is a hotter topic (well, except maybe the subsequent iterations of M1/M2).
The only stable addition to the JDK is behind us, and it's time to descend into the hell of preview features.
425: Virtual Threads (Preview)
Let's start with the news everyone has been waiting for (and which won't be a surprise to anyone reading these summaries) - in JDK 19, we finally saw the first preview of the long-awaited Loom. The community has been playing with the whole thing at its best for quite some time - now, all developers (even those who don't have time and patience for Early Access versions) can painlessly check if the virtual threads lived up to all the hype the project's subsequent announcements entailed.
I won't write extensively here - the community has long been entertained by how Virtual Threads have performed in practice. I recently devoted a sizable portion of a review to it once, so I encourage you to visit one of the previous editions. You will find an overview of the experiments that sprouted up back when JDK 19 was published in early access.
However, while a lot has been said about virtual threads, it is much quieter about another important (and related) feature:
428: Structured Concurrency (Incubator)
The long-awaited Structured Concurrency, which has just gone into incubation, is also part of Loom and is a natural complement to Virtual Threads....
What is structured concurrency? Unless one wants to try to dig into some dusty papers from the 1960s (because you can find anything there) its roots should be sought in a 2016 blog post Structured Concurrency, written by Martin Sústrik - the creator of ZeroMQ. In it, he presented the concepts of encapsulating concurrent execution threads with blocks of code that have clear start and end. The goal is to guarantee that all threads finish their work before exiting a block. This type of approach greatly facilitates reasoning about the code, as well as error handling.
The idea probably would never have gained as much popularity had it not been for Roman Elizarov, the architect of Kotlin coroutines. He decided to put it to practical use in the design of the mechanism. The results turned out to be so good that there was quite a change in thinking throughout the JVM, and - for example - the original version of the Loom design basically went into the trash. The final variant, which we wrote about two weeks ago, is already a solution fully compatible with Structured Concurrency.
In my opinion, Elizarov's talk is the best place for a broader understanding of the topic of structured concurrency.
If you think the concept looks similar to try-with-resources
what was introduced in Java 1.7 , then you have very good associations. Loom's designers decided to use it as a base for their version of structured concurrency:
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> findUser());
Future<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // Join both forks
scope.throwIfFailed(); // ... and propagate errors
// Here, both forks have succeeded, so compose their results
return new Response(user.resultNow(), order.resultNow());
}
}
In the above example, in case of any problem, the try block will automatically "clean up" all the threads created. The life cycle of each is clearly specified, and it is convenient to collect the results since the whole behaves basically like synchronous code.
Unlike Virtual Threads, published as a Preview, JEP 428: Structured Concurrency is in incubation, so the above API may evolve. What the developers want to achieve now is to get feedback from the community to refine the target API.
Okay, then, as we have Looma out of the way, we move on - Project Panama.
424: Foreign Function & Memory API (Preview)
Another ultra-heavy weight player, the effect of Project Panama, is also coming to the new JDK release. A successor to JNI is materializing in front of us, which is expected to give us new, better integration with native code and memory.
The new API is really powerful - we write about the possibilities it provides regularly (if only in the previous edition). The topic is so broad that for more details, I recommend a presentation from Oracle that covers JEP-424 in full. It has plenty of examples to help you not only better understand how to use Project Panama's first big baby, but also why you should be interested in it at all.
426: Vector API (Fourth Incubator)
Vector API can't get out of incubation ... This is the fourth time we get a redesign of it, which only proves how complicated a topic the JVM developers have taken on. Modern processors provide parallel processing capabilities, but significant differences exist between implementations. Vector API's goal is to provide a common abstraction layer over these. And one important new feature of the fourth incubator version is the use of the innovations introduced in the just-described JEP 424, as we finally got an official Preview of Foreign Function & Memory API.
If you want to learn more about the Vector API and the motivations behind it, instead of reading JEP, I recommend an episode of the Inside Java podcast. The authors of the Vector API (John Rose and Paul Sandoz) talk about the motivations behind the project, allowing you to understand better why it is so important and why its implementation is so bumpy and long.
427: Pattern Matching for switch (Third Preview)
Here (as in the case of the Vector API), the excitement is a bit smaller, as this is already the third iteration of pattern matching support for Java switches. The changes seem to be in a good direction - I like the usage of when
keyword for conditional operations. I'm waiting eagerly for Pattern Matching for switch, and I hope this is the last preview and we'll get the stable version soon. If you want more and don't like the JEP format, I recommend another video introduction from Oracle, hosted by Venkat Subramanian, a frequent (and very popular) conference guest.
The last of the new JEPs is the first Preview version of pattern matching for Records. It is expected to allow easy destructurion and (at least from my perspective) represents the first real advantage of this structure over IDE-generated POJOs. It turns out that by standardizing the way records are created and stored on the JVM, JDK developers open a path to introduce new productivity features. The first of these (and certainly not the last - I encourage you to read the document Functional Transformation of Immutable Objects by Brian Goetz) can be found in mentioned JEP.
If you are curious about the syntax, you can find it below
record Person (String name, String address) {}
if (obj instanceof Person(var name, var address)) {
println(name)
println(address)
}
Record Patterns also allow conditional casting
if (obj instanceof Person p) {
var name = p.name();
var address = p.address();
}
Inside the conditional block, we can safely use the object as if it had been cast. I suspect that this structure will be familiar if you have dealt with Kotlin in the past.
More detail can be found in JEP, and as for potential use cases, a set of very good examples can be found in Java Almanac.
Bonus: Project Lanai as default
That's not all you can expect from the new release, however. JDK 19 also marks Java's switch to a new way of rendering Desktop applications on macOS computers - OpenGL has been replaced by Metal API. This initiative's effects found their way into Java back in version 17 as part of the Lanai project, and from the current version of the JDK they were finally incorporated as default.
Phew, a lot of it. And do you know what "jars" me the most? There's a good chance that at least some of the above Preview will soon become stable with the next LTS (JDK 21). I'm already looking forward to the next ideas of all those talented engineers who, after years of working on Loom or Panama, will be able to get down to something new. It will be interesting to see what JDK 29 will look like.