Is containerization the future of Java development? Discover the answer in 2021 Containers Trend Report: Download now!

Java 17 features and tools

Pedal to the metal — Java 17 tools and features to expect


Published August 11, 2021


When we think of new Java™, we like to imagine it being like a sleek modern car from the respected series — true to its roots, but fast, comfortable, and updated with all the gizmos needed for the ride. Sure, the newer car models are introduced constantly, but when you need both reliable and practical — you can’t go wrong with this one.

So join us in exploring all the new roadways open with Java 17, and we will see where the path goes.

Java means business

Java works best when you don’t want to choose between stability, security, and support. Many years of production with developers all over the world contributing to the upstream (including those from BellSoft) means that possible security issues are found and fixed fast, the development tools are continuously upgraded, the new features are added constantly, and the outdated components are removed. Java still remains the best multi-platform solution. So using Java is the most reliable choice for business, and usually the newer the version, the better. If you are unsure about updating, contact us, and BellSoft’s engineers will examine your architecture and share their detailed expert opinion on the issue.

In any case, you can’t go wrong with Java.

So hop on, as we are about to discover what is going to be changed in the latest version.

Sleek number 17

The modern car should be devoid of the older design elements with no unnecessary parts to limit its speed but remain comfortable and powerful. And it seems the JDK community follows these guidelines too. This will become a Long Term Support release, which means it will remain an industry standard for years, and receive updates for a long time. Java 17 is expected to receive updates up till 2026. As usual for the LTS release, some things are deprecated, some features are added, and the most valuable components receive an upgrade.

Let’s explore all the changes we expect from Java 17.

Added features

Something new to go even faster.

  • Context-specific deserialization filters (JEP 415) to configure the filters via a JVM-wide filter factory. Developers can select a filter for each individual deserialization operation. The deserialization filters technology was introduced in Java 9 to enable application and library code to validate incoming data streams before deserializing them. The new context-specific approach will significantly enhance security by providing the ability to remove potentially malicious code from the deserialization process.
  • The foreign function and memory API (JEP 412) is introduced to replace the Java Native Interface (JNI) with a superior, pure Java development model. It will provide a better performance, the ways to operate on different kinds of foreign memory, and better security by disabling unsafe operations by default. In the future, it is expected to allow the usage of foreign functions written in languages other than C. JNI still functions though, so the apps depending on it will still run in Java 17.
  • Always-strict floating point semantics (JEP 306) will be restored the way they used to be in Java versions before 12. The decision to change the default floating-point semantics was made due to the restrictions of the x86 architecture of the 1990s. Since CPUs started supporting SSE2, this issue is long-solved, and making floating-point operations consistently strict will ease the development of numerically-sensitive libraries, including Java.lang.Math and java.lang.StrictMath.
  • Pattern matching for switch (JEP 406) is implemented as a preview, allowing an expression to be tested against several patterns, each with a specific action. All existing switch expressions and statements will still compile. Two new kinds of patterns are introduced.
  • A new rendering pipeline for macOS (JEP 382) will use the Apple Metal API instead of the older OpenGL. This is particularly important as there is a chance Apple will remove OpenGL API from future macOS versions. It will fit the existing Java 2D pipeline model and provide at least the same performance as the current rendering pipeline or better.

Removed features:

The older tids and bits that only slow you down will have to go.

  • Removal of the experimental AOT and JIT compiler (JEP 410). The ahead-of-time (AOT) and just-in-time (JIT) compiler was added to Java as an experiment some time ago but turned out to not be popular. As its exclusion from the JDK 16 went unnoticed, it has been decided to remove the compiler altogether, including the following modules: jdk.aot (the jaotc tool), internal.vm.compiler (the Graal compiler), and jdk.internal.vm.compiler.management (the Graal MBean). Note that while Graal is removed from Java 17, it is still an essential tool for many features, so it will keep being developed and included in builds for native images like Liberica Native Image Kit.
  • Removal of the Remote Method Invocation (RMI) Activation mechanism (JEP 407), but keeping all the rest of RMI intact. The activation mechanism is dated and disused.
  • Deprecating the Applet API for removal (JEP 398). Since all the browsers have either removed support for Java browser plug-ins or plan to do so, this now useless API also has to go. In practice, some compiled and used apps still depend on that API, which is why the API itself is still present in Java 17. If that is true in your case, you may need to stick to Java 8 (supported up to 2022) or 11 (supported up to 2023).
  • Deprecation of the Security Manager (JEP 411) for removal in a future release. This component has had a good run, as it dates all the way to Java 1.0 and was primarily used to secure the client-side code. The developers are expected to adjust the modern security solutions to do this task now. The issue is that the apps still using the Security Manager will require a lot of code rewriting when the component will actually be removed in Java 18. Since the default value of java.security.manager system property is now “disallow”, most of the tests calling System.setSecurityManager() need to be launched with -Djava.security.manager=allow.

Upgraded features

Some helpful tools that need a little polish to shine again.

  • Vector API (JEP 414), introduced in the previous version as an incubated API, will be enhanced for better performance based on the feedback from developers. It will support operations on characters, transcendental and trigonometric lanewise operations on x64 using Intel’s Short Vector Math Library (SVML), and better translation of byte vectors to and from boolean arrays. We will later discuss the implication of this feature in-depth.
  • Strong encapsulation for all JDK internals (JEP 403), except for critical internal APIs. The idea is to forbid relaxing the strong encapsulation of internal elements via a single command-line option. This, in turn, should lead to better security and encourage developers to use standard APIs instead of internal elements. In the end, it will make upgrading to the future Java versions much more effortless. The downside is that if the existing app or framework is dependent on the internal API, it may require a lot of code rewriting or even opting out of updating to Java 17. Note, that sun.misc.Unsafe and reflect classes and add-opens flag still work and may be used in the future.
  • Sealed classes and interfaces (JEP 409) that restrict which other classes or interfaces may extend or implement them. Delivered before as a preview feature, they give developers more control over the code responsible for implementing the classes they made.
  • Porting the JDK to MacOS/AArch64 (JEP 391). As we already discussed, Apple’s Mac computer’s transition to AArch64 architecture is in full progress, so the port is necessary for macOS. Note that Linux and Windows AArch64 ports are already available, and a lot of existing code is expected to be reused by employing conditional compilation.
  • Enhanced pseudo-random number generators (JEP 356) is implemented with the new RandomGenerator interface and a uniform API for all RNG algorithms, including three already implemented.

A few more words about Vector API

To see the real impact of Vector API implementation, let’s do some research.

The following test code will calculate sin(double) over an array of input arguments. This function is already implemented as a HotSpot intrinsic for x86_64. The question is, can it work even faster when using SVML? We’ll use the following jmh-based code to compare “sin” calculation with and without using VectorAPI:

/*
* Copyright (c) 2021, BELLSOFT. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*/

import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;

import jdk.incubator.vector.VectorSpecies;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.DoubleVector;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(3)
@State(Scope.Thread)

public class VectAPI {

    static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;

    @Param({"8", "16", "64", "256", "1024", "4096", "16384"})
    int size;

    double[] in;
    double[] out;

    @Setup(Level.Trial)
    public void initArrays() {
        in = new double[size];
        for (int i = 0; i < size; i++) {
            in[i] = i;
        }
        out = new double[size];
    }

    @Benchmark
    @Threads(1)
    public double[] scalarSin() {
        for (int i = 0; i < size; i++) {
            out[i] = Math.sin(in[i]);
        }
        return out;
}

@Benchmark
@Threads(1)
public double[] vectorSin() {
    int i = 0;
    int upperBound = SPECIES.loopBound(size);
    for (; i < upperBound; i += SPECIES.length()) {
        DoubleVector va = DoubleVector.fromArray(SPECIES, in, i);
        DoubleVector vb = va.lanewise(VectorOperators.SIN);
        vb.intoArray(out, i);
        }
    for (; i < size; i++) {
        out[i] = Math.sin(in[i]);
        }
    return out;
    }
}

Running this benchmark on Xeon(R) Platinum 8268 CPU @ 2.90GHz gives following results

Benchmark               (size)          Mode       Cnt      Score            Error      Units
VectAPI.scalarSin       8               avgt       15       82.495 ±         1.274      ns/op
VectAPI.scalarSin       16              avgt       15       170.337 ±        1.346      ns/op
VectAPI.scalarSin       64              avgt       15       679.862 ±        7.345      ns/op
VectAPI.scalarSin       256             avgt       15       2700.459 ±       34.977     ns/op
VectAPI.scalarSin       1024            avgt       15       11511.768 ±      194.371    ns/op
VectAPI.scalarSin       4096            avgt       15       45569.034 ±      477.101    ns/op
VectAPI.scalarSin       16384           avgt       15       188526.742 ±     1008.427   ns/op
VectAPI.vectorSin       8               avgt       15       12.815 ±         0.284      ns/op
VectAPI.vectorSin       16              avgt       15       22.056 ±         0.788      ns/op
VectAPI.vectorSin       64              avgt       15       76.410 ±         1.329      ns/op
VectAPI.vectorSin       256             avgt       15       297.379 ±        3.158      ns/op
VectAPI.vectorSin       1024            avgt       15       1344.975 ±       51.351     ns/op
VectAPI.vectorSin       4096            avgt       15       5817.973 ±       55.963     ns/op
VectAPI.vectorSin       16384           avgt       15       24212.389 ±      915.261    ns/op

In the end VectAPI.vectorSin is about 8 times better than VectAPI.scalarSin. After checking the compilation log (-XX:+PrintCompilation), we find that Double512Vector is used, which is exactly 8 doubles. It looks like linear scalability.

Let’s take a look under the hood. The main action is “sin” calculations: “va.lanewise(VectorOperators.SIN)” code for vector case. It is compiled as a call to the svml library in the end. The libsvml.so is provided with x86_64 jdk17. It contains an implementation of several vectorized functions for different vector sizes. And C2 compiler inserts calls to these functions instead of default scalar implementation in case libsvml is available.

Note that running benchmarks on older systems without AVX512 support (or emulating them by settings UseAVX=1 or UseAVX=2 to use 128-bit or 256-bit vectors) shows the worse benchmark scores proportionally to vector size reduction, resulting in an almost perfect linear scale. Disabling vectorized intrinsic by disabling UseVectorStubs to trigger scalar “sin” intrinsic shows slightly slower results than the scalar one due to additional overhead.

Our test shows that the Vector API allows the convenient usage of SVML, and it works fast. It adapts to the target CPU capabilities ( in this case - using different vector sizes and the performance scales linearly with it). Scalar calculations using code around Vector API will work, but some overhead is expected.

Changing gear to 17-th

Gearbox

We decided to ask our engineers to share their opinion on changes in Java 17. And that’s what they had to say:

I believe that one of the most essential improvements in Java 17 is the deprecation of the Security Manager that goes hand in hand with strong encapsulation for JDK internal elements and foreign function and memory API. JVM is now launched in a strict mode where it is impossible to break internal API isolation, so inaccessible modules are well protected. This mechanism takes the JDK security system to a new level.

Sergey Chernyshev

Developers will especially appreciate pattern matching for switch and Vector API, as these features make life so much better! Your code becomes more clear, concise, and easier to write and modify. In my opinion with inclusion of the Vector API Java embarks on the path to a breakthrough that we may witness in the following versions.

Dmitry Strizhikozin

The introduction of context-specific deserialization filters is a big step forward in enhancing Java security. If you work with external data, such filters are going to be your key to fix a potential weak spot for hacker attacks. In addition, strong encapsulation of JDK internals aids in safer web development. Every developer will find something that best suits their needs among new features, be it Vector API for faster calculations or a new RNG with additional interfaces. And foreign function API and pattern matching for switch facilitate and speed up code writing.

Dmitrij Pochepko

The strong encapsulation for JDK internals will help developers avoid running into the situation when it’s hard to see how the older code in the app works. It lessens the risk of using potentially insecure undocumented APIs without understanding their inner workings. I wonder how the foreign function and memory API will turn out and if it will be as easy to implement the foreign code as promised. And the Mac Aarch port shows that Java devs are trying to stay relevant, which is a good sign for the future.

Peter Zhelezniakov

Want to hear more opinions on this issue and others?

The race is on!

Java has always been a “monster car” — powerful, fast, and ready for any environment. As the years pass, it keeps gaining speed and focuses on providing the users with all the features you should expect from the modern programming language. So if you were looking for a sign to update or go Java — this is it! In a month, you’ll be able to get on this ride and enjoy the comfort and stability on your path of app development.

And we in BellSoft will momentarily provide you with our OpenJDK-based Java build with all these features and so much more. Stay tuned!

Author image

Dmitry Chuyko

Senior Performance Architect at BellSoft

 Twitter

 LinkedIn

BellSoft LTD [email protected] BellSoft LTD logo Liberica Committed to Freedom 199 Obvodnogo Kanala Emb. 190020 St. Petersburg RU +7 812-336-35-67 BellSoft LTD 199 Obvodnogo Kanala Emb. 190020 St. Petersburg RU +7 812-336-35-67 BellSoft LTD 111 North Market Street, Suite 300 CA 95113 San Jose US +1 702 213-59-59