Java 16 : what’s new ?
Now that Java 16 is features complete (Rampdown Phase Two at the day of writing), it’s time to walk throught all the functionalities that brings to us, developers, this new version.
This article is part of a series on what’s new on the last versions of Java, for those who wants to read the others, here are the links : Java 15,, Java 14, Java 13, Java 12, Java 11, Java 10, and Java 9.
This new version counts no less than 17 JEPs, but above all, sees the Records and the pattern matching for instanceof going out of preview and that’s good news!
JEP 380: Unix-Domain Socket Channels
First of all, a small description of what a Unix-Domain socket is:
Unix-Domain sockets are used for inter-process communication (IPC) on the same host. They are similar to TCP/IP sockets in many ways, except that they are addressed by filesystem path rather than by IP addresses and port numbers.
The SocketChannel
and ServerSocketChannel
classes can now be created from a Unix-Domain socket.
Example (non tested):
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(UnixDomainSocketAddress.of("path/to/socket/file"); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); //do something with socketChannel... }
More info : JEP-380.
JEP 338: Vector API (Incubator)
Vector API is a new API which allows to express vector calculations (matrix calculation among others), which will be executed via optimal machine instructions depending on the execution platform.
These optimizations include changes within the Just In Time compiler, intrinsics, and use of AVX/SSE instructions of CPUs which allow vectorization of calculations (SIMD type instructions – Single Instruction Multiple Data).
The JEP contains the following example, implemented before the Vector API by:
void scalarComputation(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f; } }
And with the Vector API by:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256; void vectorComputation(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i += SPECIES.length()) { var m = SPECIES.indexInRange(i, a.length); // FloatVector va, vb, vc; var va = FloatVector.fromArray(SPECIES, a, i, m); var vb = FloatVector.fromArray(SPECIES, b, i, m); var vc = va.mul(va). add(vb.mul(vb)). neg(); vc.intoArray(c, i, m); } }
We use here a FloatVector.SPECIES_256
which allows to manage float vectors on 256 bits. The mul
, add
and neg
operations will therefore be done via SIMD instructions on 256 bits instead of being done individually on each float.
More info : JEP-338.
JEP 389: Foreign Linker API (Incubator)
With the JEP-393 Foreign-Memory API, which allows you to manage memory segments (on heap or off heap), this new functionality lays the foundations for the project Panama by allowing the interconnection of the JVM with native code.
The Foreign Linker API makes it possible to call native code (in C for example) in an easy and performant way.
Here is an example of calling the strlen
function of the standard C library:
MethodHandle strlen = CLinker.getInstance().downcallHandle( LibraryLookup.ofDefault().lookup("strlen").get(), MethodType.methodType(long.class, MemoryAddress.class), FunctionDescriptor.of(C_LONG, C_POINTER) ); long len = strlen.invokeExact(CLinker.toCString("Hello").address())
More information on native function calls in this article by Maurizio Cimadamore: State of foreign function support.
This code may seem a bit complex, that's why jextract was created, which allows you to extract the code necessary to call a C library automatically from a C header file .
Example for calling the getpid
method of the C standard library:
echo "int getpid();" > getpid.h jextract -t com.unix getpid.h
import static com.unix.getpid_h.*; class Main2 { public static void main(String[] args) { System.out.println(getpid()); } }
More information on jextract in this article, by Sundar Athijegannathan: Project Panama and jextract.
Two new ports of the JVM
OpenJDK 16 adds Alpine Linux support and thus Musl as implementation of the C standard library for x64 and AArch64 architectures. More information in the JEP-386.
OpenJDK 16 also adds support for the AArch64 architecture on Windows (previously only supported on Linux) via the JEP 388. MacOS support on this architecture is in progress and will certainly be delivered in a future version of Java, see JEP 391.
Functionalities that go from preview to standard
The following functionalities, which were in preview (or incubator module), are now standard.
For details about them you can refer to my previous articles.
Functionalities that stay in preview
The following functionalities remain in preview (or incubator module).
For details about them you can refer to my previous articles.
Miscellaneous
Various additions to the JDK :
- JDK-8238286 : Stream.mapMulti() : allows to map an element
T
into n elementsR
via aConsumer<R>
- JDK-8180352 : Stream.toList() : accumulates the elements of the Stream in an immutable list.
- JDK-8255150 : Adds utility methods to check the range of a long index :
Objects.checkIndex(long index, long length)
,Objects.checkFromToIndex(long fromIndex, long toIndex, long length)
,Objects.checkFromIndexSize(long fromIndex, long size, long length)
. These new methods are optimized through the addition of intrinsics.
During the modularization of the JDK via the Jigsaw project, some internal APIs of the JDK that should not be usable outside of it were nevertheless made usable (for lack of alternatives, or to give applications using them a migration time). This has been called Relaxed Strong Encapsulation. A simple WARNING was displayed in the JVM logs the first time these APIs were used (behaviour that can be modified via --illegal-access
). With the JEP 396 : Strongly Encapsulate JDK Internals by Default, access to these APIs (which almost all have an official replacement in the JDK), is now forbidden by default. The default value of the flag --illegal-access
therefore changes from permit
to deny
. The flag is always present and can be modified to return to the previous behavior if necessary.
A change in preparation for inline classes of project Valhalla: JEP 390 : Warnings for Value-Based Classes. Some JDK classes are called Value-Based Classes, these classes are only vehicles for data (data carier), and are therefore likely to be transformed into Inline Classes when the Valhalla project will be released. This is the case for example of the Optional class or wrappers on primitives.
Starting from Java 16, the use of these classes in a way incompatible with inline classes will generate warnings: use of constructors (which are deprecated), synchronization, incorrect use of the == or the !=.
Performance
Many intrinsics have been added to the JVM:
- JDK-8250902 : MD5 intrinsic.
- JDK-8248188 : Base64 intrinsic.
- JDK-8173585 : Intrinsic pour StringLatin1.indexOf(char).
- Dedicated intrinsics for AArch64 (already present on other platforms): JDK-8173585, JDK-8173585, JDK-8173585.
Startup time
Java 16 has seen its share of JVM startup time optimizations. Claes Redestad, one of the Oracle engineers working on the subject, has written a rather interesting article which takes up the different optimizations of the JVM startup time since Java 8 and asks the question of the optimizations still to be done for Java 17: Towards OpenJDK 17.
Release after release, the JVM startup time has been almost halved since Java 8 (after a significant increase in Java 9 following the introduction of modules). Knowing that a JVM starts in less than 40ms, Claes wonders if it is still necessary to work on this subject?
As for Java 16, we can note, among other things, many improvements in the CDS functionality:
- JDK-8244778 : Archive full module graph in CDS
- JDK-8247536 : Support for pre-generated java.lang.invoke classes in CDS static archive
- JDK-8247666 : Support Lambda proxy classes in static CDS archive
CDS - Class Data Sharing, allows to save in an archive the classes metadata when the JVM is launched, in order to reuse them during successive launches, thus optimizing the JVM startup time.
You can find more information in my article QUARKUS, JLINK AND APPLICATION CLASS DATA SHARING (APPCDS).
The JVM contains a default archive with the metadata of certain classes from JDK, so changes to the CDS functionality automatically benefit to everyone.