JVM

Feature Freeze for JDK 22 – What Will the New Edition Bring? JVM Weekly vol. 156

Starting today, the new Java enters the Rampdown phase – this means that the feature list has been frozen and no further new features are to be expected. Therefore, we will go through the complete list of changes in the new edition.

Table of contents
Article cover

Stable

JEP 423: Region Pinning for G1

JEP 423 is designed to decrease latency in G1 through the introduction of “region pinning”. This ensures that the Garbage Collection processes don’t have to be turned off in the critical regions of the Java Native Interface.

The critical region in JNI is the particular code segment where a thread directly interacts with Java objects. Within this region, a thread has the ability to directly alter the data of the object, which is vital when interfacing with ‘unmanaged’ programming languages like C and C++. The original JEP provides a very understandable explanation of this concept.

What issue do they address?: When a thread is in a JNI critical region, the JVM has been required to prevent the movement of objects within it during the garbage collection process. G1 turns off the GC in each critical region, which greatly impacts its latency for JNI code.

Solution: JEP enhances G1 by providing the feature to pin any regions. This process includes maintaining a list of critical objects in each region: the count increases when a critical object is acquired and decreases when it is let go. Regions that have a count other than zero are deemed ‘pinned’ and are not cleared out during GC.


JEP 454: Foreign Function & Memory API

JEP 454 aims to substitute JNI with a more straightforward, comprehensible API in pure Java. It also seeks to enhance performance, offer extensive platform compatibility, and ensure the consistency and integrity of operations on native code and data.

What issue do they address? Java developers frequently have to interact with resources that are not directly managed by the JVM, particularly code that’s compiled in C languages and data stored in native memory. However, the existing APIs don’t offer a straightforward and safe method to accomplish this. This constraint hampers smooth interaction with other platforms and the utilization of native libraries and data, an area where languages like Python excel.

Solution: JEP 454 presents the Functions External and Memory (FFM API) API as a solution to these issues. This API enables Java programs to call external functions (code not within control of the JVM) and securely access external memory (memory not managed by the JVM), serving as a replacement for the unstable, unsafe, and generally unpopular JNI. The goal of the API is to allow Java programs to call native libraries and process native data in a more reliable (with the introduction of typing) and flexible manner.


JEP 456: Unnamed Variables & Patterns

JEP 456 is designed to enhance code readability and maintainability by allowing developers to “label” variables that need to be created in certain contexts, even though they may not be practically used. Additionally, it seeks to improve the handling of patterns in switch and case statements.

Original issue: It’s common for developers to declare variables they don’t plan on using, either for style purposes or due to the demands of the programming language. The unspoken belief that these variables won’t be used can cause mistakes and uncertainty in the code. They can also misleading to a variety of linters and other code assistance tools, which are becoming more and more prevalent.

Solution: JEP 456 brings in ‘unnamed’ variables and patterns. These can be utilized when there’s a need for variable declarations or nested patterns, but they are never used. Both kinds are represented by the underscore character, _. This provides a clear sign that a variable or lambda parameter is not used, thereby enhancing the readability and maintainability of the code, and offering improved tool support.

In practice: Unnamed Variable for (Order _ : orders) { // Unnamed variable total++; }

Unnamed Pattern switch (obj) { case Integer _: System.out.println("This is an Integer, but its value is not needed."); break; // Other cases... }


JEP 458: Launch Multi-File Source-Code Programs

JEP 458 simplifies the process of gradually transitioning from the development of basic programs to more complex ones. It allows you to effortlessly build and execute applications made up of multiple source files, eliminating the need for manual compilation of each one.

PS: The entire concept aligns well with 463: Implicitly Declared Classes and Instance Main Methods (Second Preview), which can be found slightly further down.

What issue do they address?: In the past, executing programs written in Java necessitated the compilation of the source code into .class files prior to running them. JEP 330 enabled the direct execution of individual source files, however, the entire program had to be contained within a single .java file, and as you know, in the JVM world…

Solution: JEP 458 enhances the ability to run programs using the java command, enabling programs that are delivered as several Java source files to be executed. Now, you can run a program made up of multiple .java files without the need for compilation, simplifying the script writing process.

In practice: If we have two files, Main.java and Helper.java, you can run Main.java using java Main.java, and the program will automatically find and compile Helper.java (in some cases it may be necessary to define a directory structure or classpath).


Discover more IT content selected for you
In Vived, you will find articles handpicked by devs. Download the app and read the good stuff!

phone newsletter image

Preview

JEP 447: Statements before super(…) (Preview)

The concept of “pre-constructor context” is introduced by this JEP. This includes both the arguments of an explicit constructor call by super and all instructions that occur prior to it. The rules for this context are similar to those of normal instance methods, with the exception that the code cannot refer to the instance being created.

What issue do they address?: In the past, constructors required the initial statement to be a call to the parent class’s constructor super(...) or another constructor of the identical class this(...). This limitation posed challenges in incorporating the necessary logic to generate arguments for super, which had to be situated in static helper methods, indirect helper constructors, or constructor arguments.

Solution: JEP 447 brings a modification in the syntax of constructors, permitting instructions that don’t reference the instance being constructed to be positioned prior to an explicit constructor call. The objective is to offer more flexibility in defining the behaviour of constructors, while ensuring that constructors function ‘top-down’ during the creation of a class instance.

In practice:

class SubClass extends SuperClass {
   SubClass(int param) {
     // Logika przed konstruktorem klasy nadrzędnej
    int calculatedValue = someStaticMethod(param);
    super(calculatedValue); // Wywołanie konstruktora klasy nadrzędnej

    // Dodatkowa logika po wywołaniu super(...)
   }

   private static int someStaticMethod(int param) {
      return param * 2;
 }}

JEP 457: Class-File API (Preview)

The aim of JEP 457 is to offer a standard API for parsing, generating, and altering Java class files.

What issue do they address?: In the Java ecosystem, class files are vital for parsing, generating, and transforming. However, current class file processing libraries like ASM, BCEL, or Javassist, often struggle to keep pace with the fast changes in the class file format introduced in the JDK. This results in version incompatibility issues and errors that application developers can see.

Solution: JEP 457 suggests a standard API for parsing, generating, and altering Java class files in accordance with the class file format outlined by the JVM specification. This will enable JDK components to transition solely to it, thereby removing the necessity for an internal copy of the ASM library in the JDK.

Why is this happening right now? The developers suggest that the progression of the JVM has significantly sped up, with an increasing number of modifications occurring directly at the generated code level. They believe that with the introduction of the new API, major projects like Valhalla will cause less disruption to the overall ecosystem.

Example of parsing class files with patterns: CodeModel code = .... Set deps = new HashSet<>(); for (CodeElement e : code) { switch (e) { case FieldInstruction f -> deps.add(f.owner()); case InvokeInstruction i -> deps.add(i.owner()); // .... and so on for instanceof, cast, etc. } }


JEP 459: String Templates (Second Preview)

JEP 459 presents String templates as a fresh expression category in Java. Text templates aid in the generation of lengthy strings by merging hardcoded text with integrated expressions and template processors to achieve pre-filled result.

What issue do they address?: The main issue is that Java programmers often construct strings by combining fixed text with expressions. Current string concatenation methods in Java, like using the + operator, are inconvenient and may result in hard-to-maintain code. On the other hand String.join is not powerful enough for complex uses.

Solution: String Templates represent a new language construct in Java, facilitating string interpolation in a more sophisticated manner compared to previous methods. They offer a secure and efficient approach to string composition. For instance, the STR template processor carries out string interpolation by substituting each embedded expression in the template with its appropriately formatted value.

Example code: String name = "Joan"; String info = STR. "My name is Joan"; assert info.equals("My name is Joan"); // true


JEP 461: Stream Gatherers (Preview)

The Stream Gatherer interface signifies the transformation of Stream elements. Gatherers have the ability to transform elements in several ways: one-to-one, one-to-many, many-to-one, many-to-many. They also have the capability to keep a record of previously encountered elements to influence the transformation of subsequent elements. Moreover, they can halt processing to transform infinite streams into finite streams, thereby facilitating their parallelized execution.

What issue do they address?: Despite the Stream API from JDK 1.8 offering a comprehensive range of intermediate and final operations like mapping, filtering, reducing, and sorting, it lacked the capability to expand this set. This implied that certain more complex tasks couldn’t be defined as streams due to the absence of the required intermediate operation.

The contributions of JEP: JEP 461 presents the intermediate operation Stream::gather(Gatherer) to the Stream API, which enables stream elements to undergo processing through a user-specified group of operations known as ‘gatherers’. This enhances the functionality of the Stream API, now capable of mapping nearly any intermediate operation.

Code example: Suppose the task is to take a stream of strings and make it unique, but with uniqueness based on the length of the string rather than its content:

var result = Stream.of("foo", "bar", "baz", "quux")
        .gather(new DistinctByLengthGatherer())
        .toList();                   

public class DistinctByLengthGatherer implements Gatherer<String, String, Set<Integer>, List<String>> {
    @Override
    public Supplier<Set<Integer>> supplier() {
        return HashSet::new;
    }

    @Override
    public BiConsumer<Set<Integer>, String> accumulator() {
        return (seen, string) -> {
            if (seen.add(string.length())) {
                seen.add(string);
            }
        };
    }

    @Override
    public BinaryOperator<Set<Integer>> combiner() {
        return (seen1, seen2) -> {
            seen1.addAll(seen2);
            return seen1;
        };
    }

    @Override
    public Function<Set<Integer>, List<String>> finisher() {
        return ArrayList::new;
    }
}

In this instance, the hypothetical operation .gather(new DistinctByLengthGatherer()) serves as an example of a Gatherer application.


JEP 462: Structured Concurrency (Second Preview)

JEP 462 presents an API for Structured Concurrency, which simplifies the management of groups of related tasks running in different threads as a single work unit. This method enhances error handling and cancellation, thereby improving reliability and observability.

The contributions of JEP: That concurrent programming can be complex, particularly when it comes to managing several tasks that are executing simultaneously. Current methods frequently face the potential hazards of thread leaks, delays in cancellation, and difficulties in observing concurrent code.

Solution: The primary class in the structured concurrency API is StructuredTaskScope from the java.util.concurrent package. This class enables you to organize a task into a set of concurrent subtasks and manage them as units within a specific structure. These subtasks run in their own threads and are then controlled, combined, and cancelled, including cascaded cancellation.

Example of use:

Response handle() throws ExecutionException, InterruptedException {
  try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Supplier user = scope.fork(() -> findUser());
    Supplier order = scope.fork(() -> fetchOrder());
    scope.join()            
         .throwIfFailed(); 

    return new Response(user.get(), order.get());
  }
}

JEP 463: Implicitly Declared Classes and Instance Main Methods (Second Preview)

Enhances the capability to write main methods straight at the level of the source file, bypassing the need to place them in a class. This simplifies the code structure, particularly for novices.

What issue do they address?: Java, despite its effectiveness in building large, intricate applications, can pose difficulties for beginners, particularly when contrasted with Python (once again). The initial stages of learning Java frequently require understandment of complex concepts that are not needed for writing basic programs.

Solution: JEP 463 brings in implicitly declared classes and main methods for instances in Java, simplifying the process of writing basic programs. It offers a more user-friendly and less complicated method for creating initial programs, all the while ensuring complete compatibility and the potential for expansion to more sophisticated language features as the programmer’s skills improve.

Practical Example:

void main() {
    System.out.println("Hello, World!");
}

This example illustrates how, with a simplified structure, it’s possible to write a program that displays “Hello, World!” without the need for explicit declaration of a class and a static main method.


JEP 464: Scoped Values (Second Preview)

JEP 464 introduces Scoped Values, which enable the secure and efficient transfer of “confined” data within a specific scope, ensuring immutability within a single thread and also between threads. They are the preferred alternative to Thread.Local especially when using a large number of virtual threads.

Primary problem: In Java, methods often require data to be passed as parameters, which can be impractical when each indirect call needs different data. Thus, a mutable context is often created, which is a kind of ‘data bag’, passed via parameters or held in thread memory.”

Solution: Scoped values enable data sharing among methods in a comprehensive program, eliminating the need for method parameters. These are of the ScopedValue type and are typically declared as final static private to hinder direct access from other classes.

The idea of doing this was taken from Common Lisp and its “special variables”.

Usage example: ScopedValue.where(NAME, <value>) .run(() -> { ... NAME.get() ... call methods ... });

In this example, the term ScopedValue.where is used to define a scoped value and the object it should be bound to. When run is called, it binds the scoped value, creating a copy that is specific to the current thread, and then executes the lambda expression that was passed. While the run call is being processed, the lambda expression, or any method that is called directly or indirectly from that expression, has the ability to read the scoped value using the get() method.

Discover more IT content selected for you
In Vived, you will find articles handpicked by devs. Download the app and read the good stuff!

phone newsletter image

Incubation

JEP 460: Vector API (Seventh Incubator)

JEP 460 expands the API that allows for the expression of vector computations, which compile into native vector instructions on supported CPU architectures, offering performance that surpasses equivalent scalar computations.

What issue do they address?: The concept of vector computing, which enables operations to be performed on multiple data simultaneously, was challenging to articulate in Java. It relied on the HotSpot auto-vectorization algorithm, which in turn limited its practical usability and performance.

Solution: The Vector API in Java enables the development of intricate vector algorithms with enhanced predictability and reliability. It utilizes the existing HotSpot auto-vectorizer, providing a more predictable model for the user.

Changes since the last incubator: The API was reintroduced for incubation in JDK 22, featuring minor enhancements from JDK 21, such as bug fixes and performance boosts. The completion of Project Valhalla is still anticipated before Preview.