What is new in Java 16 ?

September 07, 2020

Introduction

This is an alive post of what will become Java 16, and, as expected, this post will expand and change over time, until the development of Java 16 is frozen in 2020/2021. I am planning to update this post when a new feature (JEP) is targeted for JDK 16, or when there is an important update on an already targeted JEP.

If something is implemented in an incubator module, it is not a permanent feature and it is released to get feedback from developers. API in such a module may change or completely removed (not released in any future JDK release). You need to use --add-modules to use incubator modules.

If something is a preview feature, it is fully specified and implemented, but provided in a release to gather feedback, so it is not a permanent change yet. You need to use --enable-preview to use such features.

Changes

  • 2020/03/21: JDK 16 is generally available since 2021/03/16.
  • 2020/12/26: JEP 390, 396, 397 added. JDK 16 has entered Rampdown Phase One, so the feature set is frozen.
  • 2020/11/28: New features added. JDK EA build 26 (2020/11/25).
  • 2020/10/28: Proposed features added. JEP 338 examples added.
  • 2020/10/08: JEP 376, 386, 388 added.
  • 2020/09/20: JEP 387 added.
  • 2020/09/07: First post.

Java 16 Features

The list is taken from the OpenJDK JDK 16 project page.

JEP 338: Vector API (Incubator)

This is the first view of incubator module, jdk.incubator.vector, that provides vector computation (i.e. SIMD) supported by hardware instructions when available (e.g. SSE, AVX) and providing a “graceful degradation” to software implementation when SIMD hardware support is not available.

This feature will not going to provide an auto-vectorization support (i.e. you need to explicitly use the API) and it is going to be supported only on x64 and AArch64 architectures.

Here is an example with OpenJDK 16 Early-Access Build 21 (2020/10/21):

import jdk.incubator.vector.*;

public class JEP338 {

    public static void jep338() {

        int a[] = {1, 2, 3, 4};
        int b[] = {1, 2, 3, 4};
        int c[] = new int[a.length];

        // preferred length of vectors 
        // (i.e. max supported by the platform)
        VectorSpecies<Integer> SPECIES = IntVector.SPECIES_PREFERRED;

        for (int i = 0; i < a.length; i += SPECIES.length()) {

            // the vector mask is for the end-boundary
            // in case a.length is not a multiple of SPECIES.length
            VectorMask<Integer> m = SPECIES.indexInRange(i, 
                                                         a.length);
            IntVector va = IntVector.fromArray(SPECIES, 
                                               a, 
                                               i, 
                                               m);
            IntVector vb = IntVector.fromArray(SPECIES, 
                                               b, 
                                               i, 
                                               m);
            IntVector vc = va.mul(vb);
            vc.intoArray(c, i, m);

        }

      }

      public static void main(String[] args) {
              jep338();
              jep338();
      }

}

Then using (plus I compiled hsdis):

$ javac --add-modules jdk.incubator.vector JEP338.java
$ java --add-modules jdk.incubator.vector -XX:+PrintCompilation -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly JEP338 > jep338.out

we can see in jep338.out (it is a very large file):

...
============================= C2-compiled nmethod ==============================
----------------------------------- Assembly -----------------------------------

Compiled method (c2)   11638 2395             JEP338::jep338 (166 bytes)
...
0x00007feef0a5a2f9:   vpackssdw %xmm1,%xmm0,%xmm1
0x00007feef0a5a2fd:   vpacksswb %xmm1,%xmm1,%xmm1
0x00007feef0a5a301:   vpabsb %xmm1,%xmm1
...

so SIMD instructions are used.

JEP 347: Enable C++ 14 Language Features

C++ 14 features will be enabled in JDK source code builds. It basically means the code will be compiled with std=c++14 like options in all platforms (Windows, Linux, macOS, AIX).

JEP 357: Migrate from Mercurial to Git

OpenJDK project source code was kept in Mercurial, it will be moved to Git, and specifically to GitHub (see below).

JEP 369: Migrate to GitHub

Together with JEP 357, all single-repository OpenJDK projects will move from Mercurial to Git hosted at GitHub at https://github.com/openjdk .

JEP 376: ZGC: Concurrent Thread-Stack Processing

Remaining activities that is left in GC safe-points in ZGC will be moved to concurrent phase to eliminate effectively all pause and scalability issues.

JEP 380: Unix-Domain Socket Channels

Support for UNIX-domain sockets (AF_UNIX) through java.net.UnixDomainSocketAddress.

Although it is called Unix-domain socket, Windows 10 started to support it as well.

JEP 386: Alpine Linux Port

This will make a port of JDK to Linux distributions using musl as the standard library such as Alpine Linux, both on x64 and AArch64.

JEP 387: Elastic Metaspace

This will improve the use of the memory holding the class-metadata (metaspace). It will reduce the footprint and improve the efficiency of returning unused metaspace to the operating system.

JEP 388: Windows/AArch64 Port

This will make a port of JDK to Windows on AArch64.

JEP 389: Foreign Linker API (Incubator)

This API will offer a pure-Java way to access native code, replacing JNI.

I think this is a very exciting feature. Here is an example:

I have the following helloworld c code:

##include <stdio.h>

void helloworld() {
	printf("hello world!\n");
}

which is built into a shared library like this:

$ gcc -c -fpic helloworld.c
$ gcc -shared -o helloworld.so helloworld.o

and I have the following sample java code:

import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.LibraryLookup;
import java.lang.invoke.MethodType;
import java.nio.file.Path;
public class JEP389 {
        public static void main(String[] args) throws Throwable {
                var lib = LibraryLookup.ofPath(Path.of("/home/ubuntu/helloworld.so"));
                var sym = lib.lookup("helloworld").get();
                var fd = FunctionDescriptor.ofVoid();
                var mt = MethodType.methodType(Void.TYPE);
                var mh = CLinker.getInstance().downcallHandle(
                                sym.address(),
                                mt,
                                fd);
                mh.invokeExact();
        }
}

The code is pretty straightforward. You look for the library and the method symbol, and bind it to a method handle by providing a function descriptor and method type.

This can be compiled like this (since it is an incubating feature, module has to be added manually):

$ javac --add-modules jdk.incubator.foreign JEP389.java
warning: using incubating module(s): jdk.incubator.foreign
1 warning

and it can be run by java (again adding the module manually), pay attention foreign.restricted system property has to be set as well:

$ java --add-modules jdk.incubator.foreign -Dforeign.restricted=permit JEP389
WARNING: Using incubator modules: jdk.incubator.foreign
hello world!

JEP 390: Warnings for Value-Based Classes

Primitive wrapper classes, that are annotated as @jdk.internal.ValueBased, will have their constructors, which are already marked as deprecated in Java 9, will produce removal warnings.

JEP 392: Packaging Tool

Packaging Tool, jpackage, was already an incubating feature. Now it becomes a standard feature and moves to jdk.jpackage module. It provides a way to generate platform native packages such as deb and rpm in Linux, pkg and dmg in macOS and msi and exe in Windows.

Here is an example on Linux, JEP392.java is in an empty folder and contains a hello world program:

$ javac JEP392.java
$ jar cvf JEP392.jar JEP392.class
$ jpackage -i . -n JEP392 --main-jar JEP392.jar --main-class JEP392
$ ls
JEP392.class  JEP392.java
JEP392.jar    jep392_1.0-1_amd64.deb

jep392_1.0-1_amd64.deb is the package. Lets see how it is working:

$ sudo dpkg -i jep392_1.0-1_amd64.deb
$ ls /opt
jep392
$ /opt/jep392/bin/JEP392
hello world!

JEP 393: Foreign-Memory Access API (Third Incubator)

Already an incubating feature, Foreign-Memory Access API is refined and still kept as an incubating feature.

This update includes a simplified way to perform most common and/or simple tasks with MemoryAccess static class, without a need for VarHandle.

JEP 394: Pattern Matching for instanceof

Already an incubating feature, pattern matching for instanceof is finalized and becomes a standard feature.

JEP 395: Records

Already an incubating feature, Records are finalized and becomes a standard feature.

A simple example:

$ cat JEP395.java
public class JEP395 {
	static record Point(int x, int y) {}
	public static void main(String[] args) {
		var p = new Point(1, 4);
		System.out.println(p.x + " " + p.y);
	}
}

$ javac JEP395.java
$ java JEP395
1 4

JEP 396: Strongly Encapsulate JDK Internals by Default

This will change the default value of --illegal-access option from permit to deny. This means JDK internal classes other than critical APIs such as sun.misc.Unsafe will not be open by default.

JEP 397: Sealed Classes (Second Preview)

This is the second preview of Sealed Classes introduced with JEP 360 in JDK 15 with a few improvements.