Home > Software > Exploring the New Features in Java 16

Exploring the New Features in Java 16

Anastasios Antoniadis

Updated on:

Explore the exciting new features introduced in Java 16, including the Vector API, ZGC improvements, Elastic Metaspace, pattern matching for instanceof, records, strong encapsulation of JDK internals, and sealed classes. This article provides an overview of each feature, showcasing how Java 16 enhances developer productivity, application performance, and language expressiveness.

Java

Java continues to evolve with every new version, enhancing the language and platform to meet the needs of modern software development. Java 16, officially released in March 2021, brings a slew of exciting features and improvements that promise to improve developer productivity and application performance. This article delves into the key features introduced in Java 16, providing insights into how these updates can benefit Java developers.

JEP 338: Vector API (Incubator)

One of the most anticipated features in Java 16 is the introduction of the Vector API under JEP 338, currently in the incubator phase. This API allows developers to express vector computations that compile at runtime to optimal vector instructions on supported CPU architectures, significantly improving the performance of complex mathematical algorithms and processing-intensive operations. The Vector API aims to provide a platform-agnostic way to take advantage of hardware acceleration, making it easier to develop high-performance applications in Java.

Example: Using Vector API for Vector Addition

Suppose we have two arrays representing two vectors in a Euclidean space, and we want to calculate their sum efficiently. The Vector API can be utilized to perform this operation using hardware acceleration where available.

import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorSpecies;

public class VectorAdditionExample {

    public static void main(String[] args) {
        // Example vectors: a and b
        float[] a = {1.0f, 2.0f, 3.0f, 4.0f};
        float[] b = {5.0f, 6.0f, 7.0f, 8.0f};
        float[] result = new float[4]; // to store the result of a + b

        // Get the preferred species for Float vectors on the current CPU architecture
        VectorSpecies<Float> species = FloatVector.SPECIES_PREFERRED;
        
        // Perform vector addition
        FloatVector va = FloatVector.fromArray(species, a, 0);
        FloatVector vb = FloatVector.fromArray(species, b, 0);
        FloatVector vResult = va.add(vb);
        
        // Vector to array transition
        vResult.intoArray(result, i);

        // Output the result
        System.out.println("Result of Vector Addition: ");
        for (float v : result) {
            System.out.println(v);
        }
    }
}

In this example:

  • We define two float arrays a and b representing our input vectors.
  • We use the FloatVector.SPECIES_PREFERRED to obtain a VectorSpecies object that represents the optimal vector species for the current CPU architecture. This allows our code to adapt to different hardware capabilities.
  • We loop through our arrays in steps equal to the length of the vector species. This is because vector operations simultaneously process a fixed number of elements (determined by the hardware).
  • Inside the loop, we use FloatVector.fromArray to load segments of our input arrays into FloatVector instances.
  • We then use the add method on these instances to perform vector addition and store the result in our result array using the intoArray method.
  • Finally, we print the result of the addition.

This simple example illustrates how the Vector API enables developers to write more efficient numerical computations by leveraging CPU vector instructions without needing to dive into low-level intrinsics. As the Vector API evolves and moves beyond the incubator phase, it’s poised to unlock significant performance improvements in applications requiring intensive numerical computing.

JEP 376: ZGC: Concurrent Thread-Stack Processing

The Z Garbage Collector (ZGC) was introduced as an experimental feature aimed at low-latency garbage collection. In Java 16, ZGC enhances its capabilities with concurrent thread-stack processing under JEP 376. This update moves ZGC’s thread-stack processing from the safepoints to a concurrent phase, reducing pause times and improving application performance, especially for applications with a large number of threads.

JEP 387: Elastic Metaspace

Memory management significantly improves in Java 16 with JEP 387, which introduces Elastic Metaspace. This feature optimizes memory allocation for class metadata, reducing the footprint of the metaspace area and improving garbage collector performance. By returning unused class metadata memory to the operating system, Elastic Metaspace ensures more efficient memory utilization, which is particularly beneficial for applications that heavily use dynamic class loading.

Monitoring Metaspace Usage

To observe the impact of Elastic Metaspace, you can monitor the metaspace area before and after migrating to Java 16. This can be done using Java Mission Control (JMC), VisualVM, or command-line tools like jstat.

Using jstat for Metaspace Monitoring

Before Java 16: Run your application using a Java version prior to 16 and collect metaspace data. Start your application:

java -jar your-application.jar

In a separate terminal, find your Java process ID (PID) and use jstat to monitor the metaspace usage:bashCopy code

jps
jstat -gcmetacap <pid> 1000ms 

This command prints the metaspace capacity (MC), the amount of used metaspace (MU), and the maximum metaspace size (MCMN) every second. Note these values, particularly how MU changes over time and how much of the MC is actually utilized.

With Java 16: Repeat the monitoring process after upgrading your application to run on Java 16. Compare the metaspace usage to see the effects of Elastic Metaspace. Ideally, you should observe:

A reduction in the overall metaspace footprint (MC).

More efficient return of unused metaspace memory to the operating system, indicated by a tighter correlation between MU and your application’s actual metaspace needs.

Analyzing the Impact

By comparing metaspace usage before and after upgrading to Java 16, you can observe the Elastic Metaspace’s impact. Look for signs of reduced metaspace consumption and more aggressive memory reclamation. The benefits are more pronounced in dynamic class loading/unloading applications, where the metaspace area’s footprint can fluctuate significantly.

Considerations

  • Memory Tools: Besides jstat, consider using more sophisticated tools like Java Mission Control (JMC) for a deeper analysis, including heap memory and garbage collection metrics that offer insights into overall memory management improvements in Java 16.
  • Application Behavior: Monitor your application’s behavior and performance metrics other than memory, ensuring that the migration to Java 16 does not introduce regressions.

JEP 394: Pattern Matching for instanceof

Java 16 continues to enhance the language’s expressiveness with JEP 394, introducing pattern matching for the instanceof operator. This feature simplifies the common coding pattern where a developer checks if an object is an instance of a particular type and then casts it to that type. The explicit cast becomes unnecessary with pattern matching, leading to cleaner, more readable code.

if (obj instanceof String s) {
    // use s directly here
}

JEP 395: Records

Records, under JEP 395, aim to simplify data modeling in Java applications. A record class is a special kind of class that succinctly and transparently models immutable data aggregates. Records automate the boilerplate coding of data-carrying classes, such as equals, hashCode, and toString methods, allowing developers to focus on the more important aspects of their applications.

record User(String name, int age) {}

JEP 396: Strongly Encapsulate JDK Internals by Default

To improve security and maintainability, JEP 396 strengthens the encapsulation of JDK internals by making it the default behavior not to allow unrestricted access. While this change may require adjustments in applications relying on deep reflection, it encourages the use of public APIs and contributes to long-term application stability and security.

JEP 397: Sealed Classes (Second Preview)

Following its introduction in Java 15, sealed classes are included in Java 16 as a second preview with minor adjustments under JEP 397. Sealed classes allow developers to define classes and interfaces that restrict which other classes or interfaces may extend or implement them. This feature enhances type safety by providing more control over inheritance and enabling more expressive domain models.

sealed class Shape permits Circle, Square {
    //...
}

Conclusion

Java 16 introduces several features that enhance the language’s performance, security, and expressiveness. From the Vector API’s promise of hardware-accelerated computations to the simplicity and clarity offered by records and pattern matching, Java continues to adapt to modern application development needs. Developers can look forward to writing more efficient, concise, and maintainable Java code as these features graduate from experimental and preview stages to become part of the standard.

Anastasios Antoniadis
Follow me
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x