What is new in Java 9 ? Part 1: Developer Perspective

December 04, 2018

Introduction

I am a bit late to publish this post now, as Java 11 is already available and Java 12 is coming soon. I actually started writing this almost a year ago after it has been released in 2017, and I think it is important because Java 9 introduces so many things, and after Java 9, Java project has moved to 6 months release cadence, so it means after Java 9, releases will contain only a few changes. It is possible that many people still runs Java 8 which has been released in 2014, and moving to a now current or upcoming release like Java 11 or 12, you need to know all the changes in Java 9 and 10.

If you have not seen it already, I already have posts about Java 10: What is New in Java 10 ?, Java 11: What is new in Java 11 ?, and upcoming Java 12: What is new in Java 12 ?.

There are so many changes in Java 9, I decided to start with the changes that are easily visible from developers perspective, such as API changes, language changes, new algorithms or performance updates. There will be another post for all the remaining changes.

All JEPs listed here are taken from: http://openjdk.java.net/projects/jdk9/.

JEP 102: Process API Updates

Improves java.lang.Process and provides a new interface ProcessHandle and ProcessHandle.Info.

For example, we can list the Process Tree of the current process:

import java.io.InputStream;
import java.io.File;

public class JEP102 {

	private static int level = 0;

	public static void log(ProcessHandle ph) {

		ph.parent().ifPresent(JEP102::log);

		for (int i = 0; i < level; i++) {
			System.out.print(".");
		}

		System.out.println(String.format("%d [%s]", ph.pid(), ph.info().commandLine().orElse("")));
			
		level = level + 1;

	}

	public static void main(String[] args) throws Exception {

		log(ProcessHandle.current());

	}

}

This returns:

714 [su - ubuntu ]
.715 [/bin/bash ]
..5267 [/home/ubuntu/jdk-9/bin/java JEP102]

JEP 110: HTTP 2 Client (Incubating Feature)

New HTTP/1.1, HTTP/2 and WebSocket client API (jdk.incubator.http) replaces the java.net.HttpURLConnection.

This is an incubating feature (JEP 11: Incubator Modules), that means it is not a final API. It has been finalized in Java 11. See my post: What is new in Java 11 ? 321: HTTP Client Standard for more information.

JEP 193: Variable Handles

Similar to MethodHandle which is a reference to a method, VarHandle is a reference to a variable. VarHandle provides access to variables under various access modes (such as plain read/write, volatile read/write and compare-and-swap), similar to functionality provided by java.util.concurrent.atomic and sun.misc.Unsafe.

For example, assume we have a class with an int field named count. How can we atomically increment this field ? There are normally a few ways:

  • Change int to AtomicInteger
  • Use AtomicIntegerFieldUpdater
  • Use undocumented (and unsupported) sun.misc.Unsafe

Although the first two above works, they have issues like introducing additional overheads and may not fit to all use cases.

A VarHandle can be created using java.lang.invoke.MethodHandles and then one of the methods of VarHandle corresponding to memory ordering you need is used. Below is a very typical example of atomically increasing a variable, where a simple increment obj.count++ does not work correctly but VarHandle::getAndAdd fixes the problem.

public class JEP193 {

	public int count;

	public static void main(String[] args) throws Exception {

		java.lang.invoke.VarHandle vh = java.lang.invoke.MethodHandles.lookup().
			in(JEP193.class).
			findVarHandle(JEP193.class, "count", int.class);

		int errors = 0;

		boolean usevh = (args.length == 1) && (args[0].equals("vh"));

		for (int k = 0; k < 10; k++) {

			final JEP193 obj = new JEP193();

			final Thread[] producers = new Thread[10000];

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

				producers[i] = new Thread() {
					public void run() {
						if (usevh) {
							vh.getAndAdd(obj, 1);
						} else {
							obj.count++;
						}
					}
				};

			}

			for (int i = 0; i < producers.length; i++) {
				producers[i].start();
			}

			for (int i = 0; i < producers.length; i++) {
				producers[i].join();
			}

			if (obj.count != producers.length) {
				errors++;
			}

		}

		System.out.println(errors);

	}

}

If you run it without an argument, so obj.count++ is used, you will typically see errors > 0 like:

$ java JEP193
2
$ java JEP193
2
$ java JEP193
3

With vh argument, so using VarHandle::getAndAdd, there is no error:

$ java JEP193 vh
0
$ java JEP193 vh
0
$ java JEP193 vh
0

You can read even more about this in C++11 std::memory_order.

JEP 200: The Modular JDK

The JDK is modularized according to Java Platform Module System (JSR 376).

All standard modules (with a JCP spec) are named starting with “java.”. All other modules within JDK are named starting with “jdk.”.

JDK Module Graph (from: https://bugs.openjdk.java.net/secure/attachment/72525/jdk.png). Click to zoom in. Figure shows all the modules and the dependencies between them. As it can be seen, all modules depend directly or indirectly to the java.base at bottom.

JDK Module Graph (from: https://bugs.openjdk.java.net/secure/attachment/72525/jdk.png). Click to zoom in. Figure shows all the modules and the dependencies between them. As it can be seen, all modules depend directly or indirectly to the java.base at bottom.

JEP 211: Elide Deprecation Warnings on Import Statements

When a deprecated type or member is imported, the warning is not issued anymore.

import java.io.StringBufferInputStream;

public class JEP211 {

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

}

StringBufferInputStream is deprecated in Java 8, and when this class is compiled with Java 8:

$ javac JEP211.java 
Note: JEP211.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

When compiled with Java 9:

$ javac JEP211.java
... no warning ...

JEP 213: Milling Project Coin

Some of the small language changes introduced with Project Coin / JSR 334 is improved:

  • @SafeVarargs is allowed on private instance methods.
  • Final or effectively-final variables can be used with try-with-resources statements.
  • Diamond <> is allowed with anonymous classes.
  • A single underscore _ is completely removed from being a legal identifier.
  • Private interface methods are introduced.

Example below illustrates these:

import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.ArrayList;

public class JEP213 {

	// this returns the error below in Java 8
	// error: Invalid SafeVarargs annotation. Instance method f1(Object...) is not final.
	@SafeVarargs
	private void f1(Object... args) {
	}

	// this returns the error below in Java 8
	// error: <identifier> expected
	private void f2() throws Exception {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try (baos) {
			baos.write(0);
		}
	}

	// this returns the error below in Java 8
	// cannot use '<>' with anonymous inner classes
	private void f3() {
		List<String> l = new ArrayList<>() {
			private void f() {
			}
		};
	}

	// if you uncomment below in Java 9, you will receive the following error:
	// error: as of release 9, '_' is a keyword, and may not be used as an identifier
	private void f4() {
		//int _ = 0;
	}

	interface f5 {
		// this returns the error below in Java 8
		// error: modifier private not allowed here
		private int count() {
			return 0;
		}
	}

}

JEP 216: Process Import Statements Correctly

Sometimes the order of import statements affected the order of type resolution so it could be accepted or rejected just because of ordering of the import statements. This problem is fixed.

JEP 219: Datagram Transport Layer Security (DTLS)

TLS can run only over reliable transport channel, typically TCP. If an unreliable channel is used such as UDP, then DTLS (RFC 4347 and RFC 6347) should be used. Java 9 provides support for DTLS in existing SSL Engine implemention in addition to SSL and TLS.

Basically SSLContext.getInstance("DTLS") is supported now:

jshell> javax.net.ssl.SSLContext.getInstance("DTLS")
$1 ==> javax.net.ssl.SSLContext@491cc5c9

JEP 220: Modular Run-Time Images

The distinction between JRE and JDK image (think about the folders under openjdk installation) is actually small and this distinction is purely historical. A JDK naturally includes the full set of development tools. Replacing this distinction, modular tune-time image is introduced containing the following directories:

  • bin contains the command-line launchers
  • conf contains the configuration files that can be edited by developers, deployers and end users
  • lib contains the Java run-time native libraries, such as libjvm.so.

Also the release file in the root folder shows the modules included in this image, in a topologically ordered way, for example for Java 9 JDK, I have this:

IMPLEMENTOR="N/A"
JAVA_VERSION="9"
MODULES="java.base java.datatransfer java.logging java.activation java.compiler java.xml java.prefs java.desktop java.rmi java.transaction jdk.unsu
pported java.security.sasl java.naming java.corba java.instrument java.management java.management.rmi java.scripting java.xml.bind java.xml.ws.anno
tation jdk.httpserver java.xml.ws java.security.jgss java.sql java.sql.rowset java.xml.crypto java.se java.se.ee java.smartcardio jdk.accessibility
 jdk.management jdk.internal.vm.ci jdk.internal.vm.compiler jdk.aot jdk.internal.jvmstat jdk.attach jdk.charsets jdk.compiler jdk.crypto.ec jdk.cry
pto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.incubator.httpclient jdk.internal.le jdk.internal.opt jdk.jartool jdk.j
avadoc jdk.jcmd jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jlink jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.
naming.dns jdk.naming.rmi jdk.net jdk.pack jdk.security.jgss jdk.policytool jdk.rmic jdk.scripting.nashorn jdk.scripting.nashorn.shell jdk.sctp jdk
.security.auth jdk.xml.bind jdk.xml.dom jdk.xml.ws jdk.zipfs"
OS_ARCH="x86_64"
OS_NAME="Linux"
SOURCE=""

Since it is topologically ordered, java.base has to be the first module listed, because any other module depends on it.

JEP 221: Simplified Doclet API

“old Doclet API” (com.sun.javadoc) is frozen and the “new Doclet API” (jdk.javadoc.doclet) is introduced. javadoc supports both for the moment.

JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)

Interactive REPL tool and API for Java is introduced. It is called JShell, provided by the new tool jshell and the API (jdk.jshell). You see in this post also sometimes I use jshell.

JEP 224: HTML5 Javadoc

javadoc tool is updated to support HTML 5 output. HTML 4 output is still the default.

$ javadoc --help | grep -i html5

-html5        Generate HTML 5 output

JEP 225: Javadoc Search

API documentation generated by the javadoc tool now includes a (locally implemented) search feature. It can be seen in Java 9 API doc: https://docs.oracle.com/javase/9/docs/api/overview-summary.html.

JEP 226: UTF-8 Property Files

Property files are no longer needed to be converted to ISO-8859–1 with escape mechanism. UTF-8 is default.

JEP 227: Unicode 7.0

Java 8 supports Unicode 6.2, Java 9 introduces support for Unicode 6.3, Unicode 7.0 (JEP 227) and Unicode 8.0 (JEP 267). Unicode 7.0 introduces changes regarding to bidirectional behavior for languages like Arabic and Hebrew, and includes new characters and scripts.

JEP 229: Create PKCS12 Keystores by Default

Default keystore type is changed from JDK-specific JKS to industry standard PKCS12.

jshell> java.security.KeyStore.getDefaultType()
$1 ==> "pkcs12"

JEP 231: Remove Launch-Time JRE Version Selection

Because it complicates the implementation of Java launcher and it is a very rarely used feature, the support for specifying what JRE version to use to launch an application through JRE-Version manifest entry or -version: command-line option is removed.

$ java -version:8
Error: Specifying an alternate JDK/JRE version is no longer supported.
  The use of the flag '-version:' is no longer valid.
  Please download and execute the appropriate version.

JEP 236: Parser API for Nashorn

Nashorn’s ECMAScript AST API is exposed as jdk.nashorn.api so there is no need to depend on internal implementation classes.

JEP 238: Multi-Release JAR Files

Java 9 introduces multi-release JAR (MRJAR) that supports different versions of same class to be used in different Java runtime environments. The MRJAR will be marked with new manifest header:

Multi-Release: true

and JAR file will have a structure like this:

jar root
  - A.class
  - B.class
  - C.class
  - D.class
  - META-INF
     - versions
        - 9
           - A.class
           - B.class
        - 10
           - A.class

If the Java environment does not support MRJAR (e.g. Java 8), it will see A, B, C and D classes in the root. On the other hand, Java 9 will use the classes A and B under the root/META-INF/versions/9, and Java 10 will use class A under …/versions/10. Classes under higher versions are ignored whereas, if a new version is not provided, classes under lower versions are accessible. So Java 9 will access the class C and D in the root, and Java 10 will access to class B in Java 9 and class C and D in the root.

JEP 244: TLS Application-Layer Protocol Negotiation Extension

javax.net.ssl is extended to support TLS Application Layer Protocol Negotiation (ALPN) extension. For example, the selected ALPN value can be queried with SSLSocket/SSLEngine::getHandshakeApplicationProtocol.

An example:

curl -v https://metebalci.com -s -o /dev/null returns:

...
* Connected to metebalci.com (151.101.65.195) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
...

so to select and show the negotiated application protocol of http/1.1 with SSLSocket:

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
import java.io.InputStream;

public class JEP244 {

	public static void main(String[] args) throws Exception {

		final SSLContext ctx = SSLContext.getInstance("TLSv1.2");

		ctx.init(null,
			 new TrustManager[] {
				 new X509TrustManager() {
					 public void checkClientTrusted(X509Certificate[] chain, String authType) {
					 }
					 public void checkServerTrusted(X509Certificate[] chain, String authType) {
					 }
					 public X509Certificate[] getAcceptedIssuers() {
						 return new X509Certificate[0];
					 }
				 }
			 },
			 null);

		final SSLSocketFactory ssf = ctx.getSocketFactory();

		final SSLSocket s = (SSLSocket) ssf.createSocket("metebalci.com", 443);

		s.setEnabledCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"});

		final SSLParameters p = s.getSSLParameters();

		p.setApplicationProtocols(new String[]{"http/1.1"});

		s.setSSLParameters(p);

		s.addHandshakeCompletedListener(new HandshakeCompletedListener() {

			public void handshakeCompleted(HandshakeCompletedEvent event) {
				// when handshake is completed, handshaker is set to null
				// so getHandshakeApplicationProtocol returns null
				// this value is copied to SSLSocket::applicationProtocol before calling this listener
				System.out.println(event.getSocket().getApplicationProtocol());
			}

		});

		s.startHandshake();

		s.close();

	}

}

outputs the negotiated ALPN value of:

http/1.1

Warning: The example above removes the certificate verification checks, do not use anything like this in a production system.

JEP 247: Compile for Older Platform Versions

javac is enhanced to compile Java programs to run on older Java versions. Existing -source and -target option provides a way to set the source level and class level versions to be selected, however the compilation was still using the JDK of the compiler. So even if you select 8 as source and target, you could be able to compile this using a Java 9 feature in the code. New --release option provides a way to correct this, so --release 8 will give a properly compiled output supported for Java 8 execution. Supported targets for this option is 6, 7, 8 and naturally 9. The signature data needed to support this feature is kept in JDK/lib/ct.sym file.

As expected, we cannot compile JEP102 example with --release 8.

$ javac --release 8 JEP102.java
JEP102.java:8: error: cannot find symbol
	public static void log(ProcessHandle ph) {
	                       ^

JEP 249: OCSP Stapling for TLS

OCSP Stapling (requesting by clients, and obtaining and attaching the OCSP Response to the certificate by servers) is implemented. By default, it is enabled for clients, but disabled for servers.

The JEP244 example above, if run with java -Djavax.net.debug=all JEP244, displays following:

...
*** ClientHello, TLSv1.2
...
Extension status_request_v2
CertStatusReqItemV2: ocsp_multi, OCSPStatusRequest
    ResponderIds: <EMPTY>
    Extensions: <EMPTY>
CertStatusReqItemV2: ocsp, OCSPStatusRequest
    ResponderIds: <EMPTY>
    Extensions: <EMPTY>
Extension status_request: ocsp, OCSPStatusRequest
    ResponderIds: <EMPTY>
    Extensions: <EMPTY>
Extension application_layer_protocol_negotiation, protocol names: [http/1.1]
...
*** CertificateStatus
Type: ocsp
OCSP Response:
Response Status: SUCCESSFUL
Responder ID: byName: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Produced at: Sat Dec 01 18:13:00 UTC 2018
1 response:
SingleResponse:
CertId
Algorithm: SHA-1
issuerNameHash
0000: 7E E6 6A E7 72 9A B3 FC   F8 A2 20 64 6C 16 A1 2D  ..j.r..... dl..-
0010: 60 71 08 5D
issuerKeyHash:
0000: A8 4A 6A 63 04 7D DD BA   E6 D1 39 B7 A6 45 65 EF  .Jjc......9..Ee.
0010: F3 A8 EC A1
SerialNumber: [    03a30d3f 5deced01 0d9157b3 af2b944a 50bb]
CertStatus: GOOD
thisUpdate is Sat Dec 01 18:00:00 UTC 2018
nextUpdate is Sat Dec 08 18:00:00 UTC 2018
...

It shows above, certificate validation with OCSP is requested with ClientHello message, and OCSP response is attached to CertificateStatus message.

JEP 251: Multi-Resolution Images

New API (e.g. MultiResolutionImage interface) is defined in java.awt.image to support resolution variants of the same image.

JEP 252: Use CLDR Locale Data by Default

The default locale data is changed to be CLDR (provided by Unicode Consortium). JRE (or COMPAT designation) is now the second preference in the locale provider.

This is not an example of this JEP, but just to show how Locale is used:

jshell> new Locale("tr").getDisplayLanguage(new Locale("de"))
$11 ==> "Türkisch"

I do not know if COMPAT had this already, but looking at the CLDR Version 34 Locale Data Summary for German, I can see the translations, so the code above demonstrates this.

JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

The internal com.sun.* JavaFX UI Controls and CSS APIs used by many are exposed as public APIs.

JEP 254: Compact Strings

This is actually not a visible change, but I think it is important to know.

Since java.lang.String stores its value in a char array and char is encoded as UTF16, thus 2-bytes, in Java, it causes an unnecessary memory load especially if characters are mostly Latin-1, thus they can actually be encoded in a single byte. In Java 9, java.lang.String is modified to keep its value in a byte array and additionally keep an encoding identifier, which can be either LATIN1 or UTF16. If only Latin-1 characters are used, this should result ~2x improvement in memory load.

Java 8 java.lang.String contains this::

private final char value[];

whereas Java 9 java.lang.String is modified into this::

private final byte[] value;

/**
 * The identifier of the encoding used to encode the bytes in
 * {@code value}. The supported values in this implementation are
 *
 * LATIN1
 * UTF16
 */
private final byte coder;

JEP 256: BeanInfo Annotations

beaninfo Javadoc tags are replaced with annotations such as JavaBean, BeanProperty and SwingContainer.

JEP 259: Stack-Walking API

A more efficient (lazy) and capable API to walk over the stack is introduced.

A simple example:

import java.util.List;
import java.util.stream.Collectors;

public class JEP259 {

	public static void main(String[] args) {

		final List<StackWalker.StackFrame> stack = StackWalker.getInstance().walk(s ->
				s.collect(Collectors.toList()));

		for (StackWalker.StackFrame sf : stack) {
			System.out.println(sf.getClassName() + "::" + sf.getMethodName() + ":" + sf.getLineNumber());
		}

	}

}

JEP 260: Encapsulate Most Internal APIs

In order to completely eliminate the use of internal APIs, a few actions are taken.

Non-critical internal APIs, whose function can easily be replaced by a 3rd party library (e.g. sun.misc.BASE64Decoder) are encapsulated.

Critical internal APIs, whose function can only be provided by JDK, is considered according to if such functionality is provided as a public API. If such a public API exists in Java 8, they are encapsulated. If such a public API exists in Java 9, they are deprecated and will either be encapsulated or removed in the future release. If no public API exists in Java 8 or Java 9, they are not encapsulated.

Encapsulated internal APIs will not be accessible at compile time by default, but only with the use of –add-exports option. At run time, they are accessible at the moment, but this will change in the future.

JEP 261: Module System

Implements the Java Platform Module System (JSR 376). This is a very big change and it touches JEP 200, 201, 220, 260 and 282.

“link time” phase is introduced optionally between compile time (javac) and run time (java), that can be run by the link tool (jlink, JEP 282). Linking assembles the modules into a custom run-time image.

JEP 262: TIFF Image I/O

TIFF reader and writer plugins are introduced.

JEP 266: More Concurrency Updates

There are three main changes related to concurrency:

New interfaces corresponding to ones in Reactive Streams initiative is introduced.

There are enhancements to CompletableFuture API, for example, orTimeout and completeTimeout allows future to complete normally or exceptionally after a duration.

JEP 267: Unicode 8.0

Java 8 supports Unicode 6.2, Java 9 includes support for Unicode 6.3, Unicode 7.0 (JEP 227) and Unicode 8.0 (JEP 267). Unicode 8.0 introduces many new characters, blocks and scripts.

JEP 268: XML Catalogs

A standard XML Catalog API supporting OASIS XML Catalogs standard v1.1 is introduced. XML Catalogs are used to resolve external references when parsing XML/XSD/XSL files.

JEP 269: Convenience Factory Methods for Collections

As Java Language does not provide a special syntax for initializing collections (Set, List, Map), convenience static factory methods, called “of”, are introduced to make initialization of collections with a small number of elements easier. These methods return an immutable collection containing up to 10 elements provided as parameters (there are 11 overloaded functions including one with empty parameters).

jshell> List l = java.util.List.of(1)
l ==> [1]
jshell> Set s = java.util.Set.of(1, 2)
s ==> [1, 2]
jshell> Map m = java.util.Map.of("k1", 1, "k2", 2, "k3", 3)
m ==> {k3=3, k1=1, k2=2}

JEP 272: Platform-Specific Desktop Features

Java 9 adds under java.awt.Desktop to access platform specific desktop features such as macOS, Windows and Linux.

I tried the following example:

import java.io.File;
import java.awt.Desktop;

public class JEP272 {
	public static void main(String[] args) throws Exception {
		Desktop.getDesktop().browseFileDirectory(new File(System.getProperty("user.dir")));
	}
}

However, if I run it on Linux with Java 11, I get the following error. I guess if you try it on Windows or macOS, it may work.

$ java JEP272
Exception in thread "main" java.lang.UnsupportedOperationException: The BROWSE_FILE_DIR action is not supported on the current platform!
	at java.desktop/java.awt.Desktop.checkActionSupport(Desktop.java:385)
	at java.desktop/java.awt.Desktop.browseFileDirectory(Desktop.java:1034)
	at JEP272.main(JEP272.java:6)

JEP 273: DRBG-Based SecureRandom Implementations

Three DRBG mechanisms (Hash_DRBG, HMAC_DRBG, CTR_DRBG) described in NIST Special Publication 800–90A r1 is implemented.

The implementations can be instantiated through SecureRandom with algorithm “DRBG”.

I wrote a detailed post about this: Everything about Java’s SecureRandom.

JEP 274: Enhanced Method Handles

MethodHandle, MethodHandles and MethodHandles.Lookup classes are enhanced for some common use cases and better compiler optimizations.

Combinators for loops (loop) and try/finally (tryFinally) blocks are added.

For example, a loop combinator, iterative factorial function example from the Java 9 API doc:

 static int one(int k) { return 1; }
 static int inc(int i, int acc, int k) { return i + 1; }
 static int mult(int i, int acc, int k) { return i * acc; }
 static boolean pred(int i, int acc, int k) { return i < k; }
 static int fin(int i, int acc, int k) { return acc; }
// assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
 // null initializer for counter, should initialize to 0
 MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
 MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
 assertEquals(120, loop.invoke(5));

Each clause is an array of four elements, init, step, pred and fini functions. If array is shorter than 4 (as counterClause above), it is padded with nulls (so it is actually equal to null, MH_inc, null, null).

Each clause introduces an iteration variable, here we have two clauses (counterClause and accumulatorClause) so there are two iteration variables. The type of iteration variable is determined from the return type of init and step functions. If they are omitted (set to null), no iteration variable is created for that clause (a void type is used instead).

Typically init functions receive external arguments, and pred, step and fini functions receive both all iteration variables and external arguments as their arguments.

This is equivalent to:

// external variable k
// return type int (return of fini)
int loop(int k) {
  // iteration variables
  // i for counterClause
  // acc for accumulatorClause
  int i, acc;
  // counterClause has no init function (null)
  // i is implicitly initialized to 0 because it is of type int
  // init function one of accumulatorClause
  int acc = one(k)
  for (;;) {
    // step function inc of counterClause
    i = i + 1;
    // counterClause has no pred function
    // if (!true) {
      // counterClause has no return function
      // return null;
    // }
   // step function mult of accumulatorClause
   acc = acc * i;
   // pred function pred of accumulatorClause
   if (!pred(i, acc, k)) {
      // fini function fin of accumulatorClause
      return fin(i, acc);
    }
  }
}

By modifying the above example a little bit, we can see the new tryFinally combinator as well.

static int one(int k) {
  if (k < 0) throw new RuntimeException("k < 0");
  else return 1;
}
static int cleanup(Throwable t, int r, int k) {
    if (t != null) throw new RuntimeException(k + " is less than 0");
    else return 10*r;
}
// assume MH_cleanup is the method handle of cleanup
final MethodHandle tryFinally = MethodHandles.tryFinally(loop, MH_cleanup);
// cleanup modifies the return value
assertEquals(1200, tryFinally.invoke(5));
// cleanup modifies the exception
try {
  tryFinally.invoke(-5);
  fail();
} catch (Exception e) {}

JEP 277: Enhanced Deprecation

Two methods are added to Deprecated annotation to improve its meaning.
boolean forRemoval(): indicating the element marked as deprecated will be removed in future. default is false.
String since(): indicating when this element is deprecated. default is an empty string.

An example from JDK 9:
@Deprecated(since=“9”,
forRemoval=true)
public void traceInstructions​(boolean on)
The suppression of the warnings are also distinguished now as being @SuppressWarnings(“removal”) or @SuppressWarnings(“deprecation”) .

JEP 280: Indify String Concatenation

In Java 8, String concat operations are compiled into StringBuilder::append calls by the Java compiler. In Java 9, this behavior is modified and optimized.

I wrote a detailed post about this: Digging into JEP 280: Indify String Concatenation.

jlink is a new tool that can generate your custom runtime image by assembling modules.

$ jlink --module-path jdk-9/jmods --add-modules java.base --output out
$ cat out/release
JAVA_VERSION="9"
MODULES="java.base"
$ ls out/bin
java  keytool

As you can see, only java.base module is included and that also means java compiler is not included, we only have the java runtime.

As a test, we can compile a class using the java.xml module with default JDK image, then run it with the custom image I created above:

import javax.xml.parsers.DocumentBuilderFactory;

public class JEP282 {
	public static void main(String[] args) throws Exception {
		final DocumentBuilderFactory dbf  = DocumentBuilderFactory.newDefaultInstance();
	}
}
$ java JEP282 
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/parsers/DocumentBuilderFactory
	at JEP282.main(JEP282.java:5)
Caused by: java.lang.ClassNotFoundException: javax.xml.parsers.DocumentBuilderFactory
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
	... 1 more

As expected, the classes inside the java.xml module is not found.

JEP 285: Spin-Wait Hints

Java 9 introduces Thread::onSpinWait method to give JVM a hint that the following code is in a spin-loop. This has no side-effect and only provides a hint to optimize spin-loops in a processor specific manner.

I wrote a separate post about this: Spin-Wait Hints in Java.

JEP 287: SHA-3 Hash Algorithms

New hash functions defined in FIPS 202 is implemented. They are SHA3–224, SHA3–256, SHA3–384 and SHA3–512.

JEP 288: Disable SHA-1 Certificates

Due to increasing concern on certificates using SHA-1 based signatures, SHA-1 usage is disabled in certificate validators (CertPathValidator, CertPathBuilder, TrustManagerFactory) shipped with SunX509 and PKIX providers.

Because SHA-1 is still widely used, this change is implemented with enhancements on jdk.certpath.disabledAlgorithms security property, namely with jdkCA, denyAfter and usage constraints.

The security config (java.security) shipped with Java 9 contains this:

jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & usage TLSServer, RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224

So, SHA1 algorithm is disabled for use in certificates validated against JDK pre-installed trust anchors (in cacerts keystore) (jdkCA) for TLS server certificate chains (usage TLSServer).

JEP 289: Deprecate the Applet API

Since Browsers are removing the Java Applet support, the Applet API is deprecated.

JEP 290: Filter Incoming Serialization Data

A way to persist and then load objects are to use ObjectOutputStream and ObjectInputStream. Now, it is possible to set a filter on ObjectInputStream so a deserialization can be terminated before the object in the stream is actually instantiated.

For example, the code below:

import java.io.Serializable;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectInputFilter;

class JEP290 implements Serializable {

	public static class Dummy implements Serializable {
	}

	public JEP290() {
	}

	public static void main(String[] args) throws Exception {

		final ByteArrayOutputStream baos = new ByteArrayOutputStream();

		final ObjectOutputStream oos = new ObjectOutputStream(baos);

		oos.writeObject(new JEP290());
		oos.writeObject(new Dummy());

		oos.close();

		final ObjectInputStream ois = new ObjectInputStream(
				new ByteArrayInputStream(
					baos.toByteArray()));

		ois.setObjectInputFilter(
				new ObjectInputFilter() {

					public Status checkInput(FilterInfo filterInfo) {

						if (filterInfo.serialClass() != JEP290.class ) {

							return Status.REJECTED;

						} else {

							return Status.UNDECIDED;

						}

					}

				});

		final Object jp1 = ois.readObject();
		final Object jn1 = ois.readObject();

		ois.close();

	}

}

terminates with:

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
 at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1278)
 at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1860)
 at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1737)
 at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2024)
 at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1559)
 at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:423)
 at JEP290.main(JEP290.java:51)

JEP 292: Implement Selected ECMAScript 6 Features in Nashorn

A number of features of ECMAScript 6 is integrated into Nashorn. You can see the list of features in the JEP page here: https://openjdk.java.net/jeps/292.

JEP 295: Ahead-of-Time Compilation

In order to improve the start-up time of Java applications, classes can be compiled into native libraries before JVM is launched and they can be used afterwards rather than bytecodes or JIT compiler.

For example, HelloWorld.java is a simple Java HelloWorld class printing Hello World ! and first we compile it to bytecodes:

$ javac HelloWorld.java

Then we use the new jaotc, Java AOT Compiler, tool. The input can be class names, class files, modules, JAR files or directories containing class files.

$ jaotc --output libHelloWorld.so HelloWorld

Then, we run the program like this:

$ java -XX:AOTLibrary=./libHelloWorld.so HelloWorld

In order to see if AOT is used:

$ java -XX:AOTLibrary=./libHelloWorld.so -XX:+PrintAOT HelloWorld
     12    1     loaded    ./libHelloWorld.so  aot library
     73    1     aot[ 1]   HelloWorld.<init>()V
     73    2     aot[ 1]   HelloWorld.main([Ljava/lang/String;)V
Hello World!

I also have a detailed post about this: Demystifying the JVM: Interpretation, JIT and AOT Compilation.