I've watched all the talks from JVMLS so you know why it's worth to do it - JVM Weekly vol. 97
Today we have one topic - JVMLS.
JVMLS (Java Virtual Machine Language Summit) is a conference focused on topics related to the JVM, such as programming languages, tools, and optimization techniques. I decided to watch the available presentations from JVMLS to share with you which ones are worth watching.
Java in 2024
Alright, let's start with the Keynote Java in 2024, where Georges Saab, Senior Vice President (Java Platform Group - Oracle) and leader of the Java group at Oracle, breaks down the evolution of the Java release process. He references the "old, bad times" when infrequent and unpredictable updates led to feature backlogs and instability. To address this, about a decade ago, the Java team transitioned to a model of frequent releases alongside Long-Term Support (LTS) versions (which, I must note, is a somewhat surprising simplification—more on this topic here). Despite initial skepticism and concerns about management and quality, this change has allowed for regular, reliable updates without the need for rushed implementations—something everyone has come to appreciate over the years. The introduction of the modular system in JDK 9, though initially criticized, turned out to be key in managing dependencies and ensuring stability.
Georges also addresses the challenges of keeping external libraries and frameworks up to date with the latest JDK versions, especially those closely tied to JVM internals. The lack of support for new APIs also causes some segmentation within the ecosystem. To counter this, the Keynote suggests that library maintainers adopt a release model similar to the JDK, offering both new versions and LTS options. Although aware of the skepticism surrounding this approach, Georges believes it could provide users with greater flexibility and a better overall experience, and he invites more library maintainers to adopt this strategy. I'm curious if we'll see a surge of such solutions in the future.
Project Babylon - Code Reflection
The next two presentations are about the concept of Code Reflection.
We start with Project Babylon - Code Reflection, where Paul Sandoz introduces Babylon, which aims to support various transformations and analyses of application sources through code reflection—a development of the existing reflection mechanism that provides deeper access to code inside method bodies and Lambda expressions. This is achieved through a symbolic representation known as the Code Model, which organizes instances of Java classes into a tree structure, along with APIs for modifying it. I wrote more extensively about the project last year, during the last JVMLS.
Paul Sandoz discusses the challenges of designing these code models to accommodate different use cases and plans to provide APIs for building, traversing, and transforming these models. He delves into the technical details, explaining that access to code is standardized through annotations like "ACT code reflection," allowing the reflection mechanism to obtain a method object and its code model. During compilation, annotations are added, and the Java compiler generates both bytecode and a code model, which is stored in the class file as a serialized string.
Paul points out that not all Java language constructs can be fully reflected in code models, making the current solution partial. Despite this, he highlights the significant possibilities offered by integrating code models with different compilers, such as NVVM for GPU optimization, which could enable translating code into other languages like OpenCL, CUDA, or even SQL. Interestingly, Paul also shows that the creators are not committed to a single solution—alternatives, such as self-transforming code models, are also being considered, which could eliminate the need for standardization and simplify the transformation process.
Code Reflection in Action - Translating Java to SPIR-V
We've just had some theory, now it's time for some practical and implementation examples.
Code Reflection in Action - Translating Java to SPIR-V by Steve Dohrmann from Intel discusses using code reflection to translate Java to SPIR-V, focusing on various methods, dialects, and programming models for GPUs. SPIR-V is a binary format—a standard intermediate representation—that facilitates optimization and efficient code execution on different GPU architectures. The presentation goes through the steps of using code reflection in the context of translating Java, focusing on aspects such as creating a new SPIR-V dialect to simplify transformations, necessary operations like implementing operations and types, and using the Beehive SPIR-V toolset for binary translation. It also discusses translating Triton DSL for GPU execution, highlighting the importance of formally defining programming models for effective translation.
Besides presenting the advantages of SPIR-V itself, which Steve argues is well-suited for expressing parallel computations and graphics, Steve demonstrates the adaptability of the code reflection API to various programming models. The translation process is modular and occurs in two main phases: converting Java code to a new dialect, then to binary SPIR-V. The ability to create new SPIR-V dialects simplifies the translation process, ensuring better structural and semantic representation of Java code, taking into account the current limitations of Code Models.
Overall, it’s great to see, aside from the theory from the previous presentation, a closer look at the more practical aspects (and problems) and toolsets that enable users to write high-level GPU code while hiding the complex transformations.
Rethinking Java String Concatenation
We've been up in the clouds, now it's time to come back down to earth. We'll be talking about string concatenation.
In Rethinking Java String Concatenation, Claes Redestad, Principal Member of Technical Staff (Java Platform Group - Oracle), discusses improving the performance of string concatenation in Java, the challenges associated with using invoke dynamic
, and the optimizations introduced in JDK 23.
The introduction of invoke dynamic
in JDK 9 significantly improved performance, making string concatenation up to three times faster than before. However, this transition also came with challenges, such as increased startup overhead and high resource usage in the JIT compiler, especially with complex expressions. The performance drop with complex concatenations highlights the need to simplify constructs to support JIT compiler efficiency. Although invoke dynamic
is a powerful tool, its limitations become apparent in scenarios with high arity expressions (the number of arguments a function or operation can take).
However, the new features in JDK 23 address these issues by simplifying the transformation of complex expressions into more manageable classes, aiming to reduce the number of generated classes and improve performance. Benchmark results emphasize the importance of minimizing Java application startup time, especially with complex string operations. Interestingly, hidden classes—a concept introduced in JDK 15—were used to achieve these results, allowing for the creation of dynamic, temporary classes that are invisible to the application and not tied to class loaders. They are mainly used for runtime code generation, such as in utility libraries that need to create classes on the fly without polluting the application's namespace. Hidden classes are not assigned to packages, cannot be persisted, or deserialized, making them ideal for advanced use cases like dynamic class modifications and proxy creation (e.g., for concatenation), where isolation and temporariness are key. It's nice to see that all these functionalities we read about in new JDK versions find their use.
Key takeaways from the presentation: while recent optimizations have improved string concatenation, continuous performance testing is essential to identify and eliminate performance regressions.
An Opinionated Overview on Static Analysis for Java
Surprisingly, An Opinionated Overview on Static Analysis for Java by Christian Wimmer, GraalVM Native Image Architect, was probably my favorite presentation of all at JVMLS. An incredible amount of meat to it, making it difficult to summarize, but I'll try to convey the main message.
Static analysis of Java, compared to dynamic analysis, aims to precisely determine all potentially accessible methods, which is challenging in dynamic languages like Java. Static analysis often uses over-approximation, predicting more possibilities than actually occur to account for all possible cases, while dynamic analysis typically under-approximates, predicting fewer possibilities, focusing on actual cases.
Basic approaches, such as class hierarchy analysis, which examines the inheritance structure of classes to identify potential method calls, can quickly identify these calls. More advanced techniques, like points-to analysis, which tracks what objects are available at different points in the program, offer greater precision but are more complex and time-consuming.
Introducing compiler optimizations before static analysis can simplify the input data, significantly improving the accuracy of analysis results. However, handling dynamic features, such as JNI and reflection, requires careful configuration and trust in the types available to the mechanism, as they can introduce additional complexity. Hybrid approaches that combine fast type analysis with points-to analysis allow for a balance between speed and precision, making them, according to Christian, suitable for larger applications. Therefore, it's crucial to find the right balance between the complexity of the analysis and its efficiency to avoid developer frustration while waiting for builds.
Valhalla - Where Are We?
I had a false start a week ago, because now I have to return to both Valhalla and Leyden. Let's start with Valhalla.
In the presentation Valhalla - Where Are We?, Brian Goetz discusses the current state of Project Valhalla, focusing on optimizing performance through improving memory layout and simplifying the distinctions between primitive types and objects. The project has already developed several prototypes (available, among others, in early access versions of JDK, which I linked to last week), and an initial implementation of value classes, which have no identity and only serve to store state, enabling more efficient data processing and reduced memory usage. For example, current tests indicate a significant acceleration in memory management operations, which is a promising step towards further optimizations.
In the future, Project Valhalla plans to introduce non-nullable types, which will eliminate potential errors related to using null values. Additionally, relaxed atomicity for larger value types is planned, enabling performance closer to primitive types while maintaining data safety and integrity. These changes aim to further align the semantics of primitive types and objects, simplifying the programming model in Java.
Although specific release dates for new Valhalla features have not been officially announced yet, work is progressing according to plan. Key features, such as full support for value classes and non-nullable types, are to be gradually introduced in future JDK versions. It is anticipated that the most significant improvements will be available within the next two to three years.
Project Leyden Update
We've had Valhalla, so now it's time for the Project Leyden Update by Ioi Lam, HotSpot JVM Engineer (Java Platform Group - Oracle), and Dan Heidinga, Software Architect (Java Platform Group - Oracle).
As I described last week, Project Leyden currently focuses on speeding up the startup and warm-up times of Java applications, allowing applications to start working faster and reach full performance in less time. The project has already introduced an Early Access version, available since June this year, which enables testing of pre-implemented features. Key elements include AOT class loading, meaning classes are loaded into the JVM before the application starts, and dynamic call optimizations. Thanks to these improvements, initial tests have shown significant startup time improvements in popular frameworks like Helidon, Micronaut, Quarkus, and Spring Boot, achieving up to a threefold speedup.
But that's not the end of Leyden's story. Future plans include further development of Ahead-of-Time techniques, particularly AOT code compilation, making application start and warm-up even faster. There are also plans to implement advanced method profiling, which will allow saving and using behavior data from previous runs to optimize code. This will enable applications to reach full performance faster and manage resources more efficiently. Key optimizations include pre-loading and pre-linking classes before the application starts, saving time by bypassing the usual resolution and linking processes during runtime. The Leyden team also plans to use profiles from previous runs to optimize future execution paths (similar to PGO).
Additionally, the presenters focus heavily on the dynamic nature of Java, which balances between static and dynamic behavior to optimize performance. Using speculative optimization—making assumptions about the future behavior of an application based on its past performance—Leyden can shift work to different phases, improving startup and warm-up times without imposing significant constraints on applications.
Although the full release of these functionalities does not have a set date yet, the Leyden team plans to iteratively implement new features, continuously improving performance and compatibility—much like what is happening with Valhalla.
Type Specialization of Java Generics - What If Casts Have Teeth
Last but not least—generics, and the presentation Type Specialization of Java Generics - What If Casts Have Teeth by Rémi Forax, Professor at Université Gustave Eiffel in Paris (whom you might recognize for stirring the pot on JDK mailing lists), and Loris Créantor, a PhD Student from the same university. They started with a telling quote: "We have no solution, we have only problem," but in reality, the presentation contained several genuinely interesting insights.
Rémi and Loris explore the concept of specialized generics in programming, focusing on improving how value types from Valhalla and generics interact at runtime. Currently, value types are optimized only at runtime, creating a mismatch when used with generics. Generics are erased at runtime, meaning detailed type information is not available, leading to inefficiency and inconsistency. The main goal is to resolve this mismatch and optimize how generics handle value types to improve performance and consistency.
The proposed solution involves changing the current implementation of generics to allow type specialization. This would mean adjusting how generics are implemented, which could involve several levels of changes. At the most basic level, type arguments may only be needed to verify that a field is not empty before writing to it. More complex solutions could include type checking at method entry and exit or even ensuring that casts are valid according to specialized types. The researchers have developed a prototype (yes, they have!) to test these various levels of specialization and measure their impact on existing code.
One of the main challenges is maintaining backward compatibility. Since the project involves changes to the fundamental way generics and types are handled, there's a risk that existing code may not work correctly if it relies on current behavior. Researchers have already found that even well-maintained code in the Java Development Kit (JDK) fails tests for the proposed type specialization, particularly in cases related to method input and output checking or singleton patterns. Another challenge is ensuring that the solution will be practical and feasible to implement across different projects and codebases without requiring significant rewrites or adjustments, which could hinder its adoption. The complexity of handling separate compilation and maintaining performance while adding type checks also poses a significant challenge.
No wonder we've been waiting so long for Valhalla.
That's probably everything, unless the JVMLS playlist surprises me with something else (I wish the above video would automatically open for you now).
And now that we've got the official part out of the way, a small recommendation: check out Master Software Architecture: A Pragmatic Guide.
I'll be honest, since reading "Foundations of Scalable Systems" (a decent book but part of a trend of very similar architecture books), I promised myself to take a longer break from the topic. At the same time, I know
- the author , I've seen his presentations, and I've read (and recommend for some time) his Substack blog , so I decided I couldn't pass up his new book, Master Software Architecture: A Pragmatic Guide, if only to try and break away from this trend.What I found is a very good book on Software Engineering -> Software Architect, along with the complications that come with a new "workstream" (because, in my mind, architect isn't necessarily a role), more like a "Pragmatic Architect," which fits well with the title. It's something I can recommend to Regular+/Senior people in the company if they need condensed knowledge about what software engineering is like at a slightly higher level of abstraction.
What surprised me, though, was how many practical principles and useful tips I found for myself. Maciej has linked together most of the trends you've seen at conferences (Domain Storytelling, Event Storming, User Story Mapping, mapping Bounded Contexts) into one digestible whole. What especially charmed me was the number of useful diagrams, behavior schemes, templates, and ready-to-use practices. The older I get, the more I appreciate opinionated books—after 10+ years in the industry, I've learned that "it depends," and I value the coherent approach adopted by a specific person. That's why I liked "The Engineering Executive's Primer" by Will Larson, and that's why I like "Master Software Architecture: A Pragmatic Guide" as well. Maciej isn't afraid to have opinions, and that's a Very Good Thing.
Overall, it's probably my favorite "the next book" on Domain-Driven Design. And even though I'm not a big DDD enthusiast, I think that because of its practicality, I'll be able to incorporate many of the concepts into my daily work.
All the links were no affiliation, "clean" ones. I just like both Maciek's work and the book 😉.