Loom Strikes Again: What improvements to Virtual Threads we will see in 2024 - JVM Weekly vol. 72
Today we return again to topics related to changes coming to Java later this year.
1. What did Alan Bateman say about the future of Virtual Threads?
If you think that the story of the rapid development of Virtual Threads came to an end with JDK 21, I have a bit of a surprise for you. In fact, during the FOSDEM conference, Alan Bateman, one of the JDK Architects, showed the progress of Project Loom.
Instead of focusing on the current state, however, he took a few steps forward in his talk, presenting the challenges facing the project. The main problem with the current iteration of Project Loom, which Bateman highlighted, was the issue of threads pinning.
With the stabilisation of virtual threads in JDK 21, the community has generally welcomed the new feature with enthusiasm. Conceptually, it is hard to find opponents to the current design, with most sceptics focusing on the 'pinning' problem associated with synchronised methods, JNI sections or Object.wait
instructions. Pinning occurs when a virtual thread, while holding a monitor/"lock", tries to free up compute for another virtual thread, but is unable to do so. This happens because the JVM's implementation of the monitor binds the 'carrier thread' (as Alan described it in his presentation - the "platform" giant carrying its "virtual" one on its back) in such a way that it cannot free the monitor and continue with other tasks. This problem, particularly apparent when a virtual thread either parks during a synchronised method to perform operations such as I/O socket, or locks up trying to enter a synchronised method. This potentially causes performance degradation, scalability issues, jamming, corruption and starvation.
The whole problem is a difficult one to solve, as most of the challenge is posed by locking mechanisms that are obsolete from today's perspective and have existed in the VM for years. To handle some of the boundary conditions of Virtual Threads, it proved necessary to upgrade the entire locking mechanism in Java, which is an arduous process and will probably take another couple of years, looking at the experience of other such initiatives. In the meantime, Patricio Chilano Mateo of the Hotspot team has introduced plan B. This is implemented by 'swapping' the owner identifier of the virtual thread with the classic Java thread (a lovely hack) in the lock, which in itself introduces overhead (you have to dig into the relevant structures and swap the value in question), but at the moment seems to solve the pinning problem quite effectively (at least in some places). You can find discussions on this topic in this thread of the JDK mailing list. .
Bateman also presented a new approach to managing IO requests. Until now, this one was implemented as a platform thread, which only passed work to virtual threads, which basically ended up with a context switch every time. The new solution is to make Pooler itself a virtual thread, so that (in most cases) queries can be executed in the same thread that accepted them.
The whole concept was inspired by the paper User-level Threading: Have Your Cake and Eat It Too, in which researchers at the University of Waterloo described different approaches to I/O strategies. This one is not publicly available, but an approximation is a video presentation by the creators, probably sufficient for the 99% of people who don't happen to implement pooling in the language in their current sprint ;).
In addition to these specific improvements, Alan also touched on broader changes to the concurrency landscape, such as improvements to the Fork-join pools to make better use of resources, especially on systems with fewer cores.
Talking about conference talks, the other one worth watch is certainly Java Language Update - Early 2024 Edition. In it, Viktor Klang, another Software Architect from Oracle working in the Java Platform Group, weaves a tale of how individual changes to Java, disconnected in theory, make sense as a whole and drive the language in a new and very coherent direction. I believe this is a must-see session for anyone who wants to better understand what picture is emerging from the individual puzzles.
2. New JEPs: Flexible constructors and derived records
JEP Draft: Flexible Constructor Bodies (Second Preview)
This JEP by Archie Cobbs and Gavin Bierman is an evolution of JEP 447: Statements before super(...), which is yet to be released in JDK 22, and has already managed to change its name and will undergo some changes.... and such surprisingly interesting ones. On the face of it, we're not talking about any revolutions. JEP proposes to make the syntax of Java constructors even more flexible - as long as it allows instructions in constructors that don't refer to the instance being created, as long as they are not read before the constructor is explicitly called. However, the motivation behind this change is interesting - according to the JEP content, this will be required by Value Classes, which will be implemented as part of Project Valhalla. For now, we don't know too much more, but once again reviewing the JEP updates we see a suggestion that we will probably see some of the first publicly available versions of Project Valhalla within the JDK by the end of the year.
In addition to the above, the JEP also mentions as yet unspecified changes to the handling of local classes, but we will probably have to wait until the next iteration for details of such. Other than that, it seems that the rest of the design of the project remains unchanged.
JEP draft: Derived Record Creation (Preview)
The second of today's JEPs is about a long-awaited streamlining of how Java records work and bringing them closer to equivalents in other modern programming languages.
JEP Draft: Derived Record Creation (Preview) in introduces the concept of derived record, which can be created from existing record instances. Records, being immutable by design, often require the creation of new instances to reflect changes in the data, leading to a popular pattern of so-called wither methods:
record Point(int x, int y, int z) {
Point withX(int newX) {
return new Point(newX, y, z);
}
Point withY(int newY) {
return new Point(x, newY, z);
}
Point withZ(int newZ) {
return new Point(x, y, newZ);
}
}
The new JEP proposes a syntax that allows programmers to concisely express these modifications in a block of code designated for this purpose, increasing the expressiveness of the language and reducing the repetitive code associated with handling immutability.
An example is worth a thousand words, so this is how we can use Derived Records:
record Point(int x, int y, int z) { }
var nextLoc = new Point(3,3,3)
Point finalLoc = nextLoc with { x = 0; };
/// finalLoc: Point(0,3,3)
Obviously nothing is ever truly straightforward, and so in this case the creation of such derived records will be subject to certain rules and boundary conditions, for these however I refer you to the original JEP.
Since I have already mentioned Kotlin, it's interesting to compare its approach to solving a similar problem. Kotlin introduces data classes designed for data storage, similar to Java records. These classes come with a built-in copy
method, enabling the easy creation of modified instances. However, the creators of **Derived Records** have opted for a more flexible transformation approach with their proposed with
block. For example. it allows operations on nested records, as illustrated in an example from JEP:
Marker scaled = m with {
loc = loc with {
x *= 2;
y *= 2;
z *= 2;
}
};
In addition to the above, the recently described JEP 465: String Templates and JEP 466: Class-File API (Second Preview)have received candidate status.
3. Release Radar
Docker Desktop 4.27
Surprised to see Docker Desktop here?
You shouldn't be, as Docker Desktop 4.27 now natively supports the Testcontainers framework, thanks to its recent acquisition of AtomicJar. This version leverages Enhanced Container Isolation, a premium Docker Desktop feature designed to bolster container security by implementing additional isolation mechanisms. Essentially, this aims to significantly limit the potential for malicious code in one container to impact the host system or other containers. For a more detailed, but approachable explanation (albeit still within the realm of operating systems and requiring some Unix knowledge), check out the official Docker documentation.
Another notable update for Java developers in Docker Desktop 4.27 is the stabilisation of the new docker init
command. This tool simplifies the initial configuration of Docker projects by automatically generating a Dockerfile, Compose and .dockerignore
files, tailored to the needs of the project. It tailors it to the needs of the project, and while the options are fairly limited in Java for now (for example, the current version of Dockerfile
uses Maven and Eclipse Temurin and it's not configurable), the file generated is impressive anyway, especially the mass of comments and links in Dockerfile
or the size of .dockerignore
.
So it's worth using docker init at least once - if only for educational purposes how such a Dockerimage should look like.
Micronaut 4.3
Key enhancements Micronaut Framework 4.3 is an improved integration with Kotlin Symbol Processing (KSP), incorporating fixes from the latest Kotlin version 1.9.20. Also introduced are new modules such as Micronaut Chatbots (obviously) and Micronaut EclipseStore, providing integrations with the successor to the MicroStream project. In addition, the release improves conditional caching and enables multi-inheritance configurations using the framework's expression language..
Of course, it doesn't stop there, because as is the case with every new Micronaut release, 4.3 brings updates across its ecosystem, including cloud integration modules for AWS, GCP, Azure and Oracle Cloud, offering enhancements and new functionality such as Push Pub/Sub subscriptions for GCP. The used version of GraalVM Native Image Build has been updated, Docker and Java 21 support has been improved, and Micronaut's Bill of Materials (BOM) now enables easier dependency management by including versions from different modules. Updates got database migration tools along with fixes to various modules such as Micronaut Data, gRPC, Kafka.
Amper 0.2.0
As a wrap up, we have an update to the Amper project, which is an experimental tool for declarative configuration of project builds created by JetBrains (you can find more about the project here). It has just received an update to version 0.2.0. I probably wouldn't mention it - as it's a rather small upgrade - but it's an opportunity to talk about a little lesser-known Gradle functionality.
Amper introduces support for Gradle Version Catalogs, a feature introduced in Gradle 7.0 that allows you to centrally manage versions of libraries and plugins, making it easier to manage dependencies in multi-module projects. This way, instead of repeating versions of libraries in different modules, they can be defined once in the version catalogue and referenced using aliases. Ampera now allows access to dependencies declared in these directories, using the syntax $libs.library.name
.
IDEs that support Ampera, including IntelliJ IDEA, Android Studio, and JetBrains Fleet, now feature dependency auto-suggestion powered by JetBrains Package Search. Moreover, starting with IntelliJ IDEA 2024.1, users can create new Ampera-based Kotlin projects directly from the New Project Wizard.
PS: The last issue was sent on Monday because.... I forgot to click the send button on Thursday when it was created. Mea Culpa.