Java 9 Is Coming!

27th Of July 2017

Public Service Announcement

Lots to talk about!

Module System
Language Changes
New and Updated APIs
New JVM Features
Performance Improvements

Module System

Delaying Java since 2008!

JAR Hell is bad, mkay?

JARs have:

  • no explicit dependencies

  • no well-defined API

  • no concept of versions

Some consequences:

  • NoClassDefFoundError

  • no encapsulation across JARs

  • version conflicts

Modules

Modules are like JARs but have:

  • explicit dependencies

  • a well-defined API

  • no concept of versions

Some consequences:

  • reliable configuration

  • strong encapsulation

  • a couple of nice features

  • still version conflicts 😭

Platform Modules

platform modules

Module Descriptor

A file module-info.java:

module java.sql {
	requires transitive java.logging
	requires transitive java.xml
	uses java.sql.Driver
	exports java.sql
	exports javax.sql
	exports javax.transaction.xa
}

Reliable Configuration

module java.sql {
	requires transitive java.logging
	requires transitive java.xml
}

Module system enforces:

  • all required modules are present

  • no ambiguity

  • no static dependency cycles

  • no split packages

Strong Encapsulation

module java.sql {
	exports java.sql
	exports javax.sql
	exports javax.transaction.xa
}

Say you want to access java.sql.ResultSet.

Module system only grants access if:

  • ResultSet is public

  • java.sql is exported by java.sql

  • your module reads java.sql

Services

The module system as a service registry:

module java.sql {
	uses java.sql.Driver
}

module mysql.driver {
	provides java.sql.Driver
		with com.mysql.MySQLDriver;
}

Loading Services

Code in java.sql can now do this:

List<Driver> drivers = new ArrayList<>();
ServiceLoader
	.load(Driver.class)
	.forEach(drivers::add);

Other Features

  • finer grained dependencies and exports

  • open packages and modules (for reflection)

  • unnamed and automatic modules (for migration)

  • layers (for containers)

  • jlink to create runtime images

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

Enabling reuse between default methods.

No Reuse

public interface InJava8 {

	default boolean evenSum(int... numbers) {
		return sum(numbers) % 2 == 0;
	}

	default boolean oddSum(int... numbers) {
		return sum(numbers) % 2 == 1;
	}

	default int sum(int[] numbers) {
		return IntStream.of(numbers).sum();
	}

}

Private Interface Methods

public interface InJava9 {

	private int sum(int[] numbers) {
		return IntStream.of(numbers).sum();
	}

}

Just like private methods in abstract classes:

  • must be implemented

  • can not be overriden

  • can only be called in same source file

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

Making try-with-resources blocks cleaner.

Useless Variable

void doSomethingWith(Connection connection)
		throws Exception {
	try(Connection c = connection) {
		c.doSomething();
	}
}

Why is c necessary?

Why is c necessary?

  • target of close() must be obvious
    ⇝ resource should not be reassigned

  • easiest if resource is final

  • easiest if resource must be assigned
    and can be made implicitly final

try(Connection c = connection)

Effectively Final Resource

But since Java 8 we have effectively final!

This works in Java 9:

void doSomethingWith(Connection connection)
		throws Exception {
	try(connection) {
		connection.doSomething();
	}
}
  • compiler knows that connection is not reassigned

  • developers know what effectively final means

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

A little more type inference.

Diamond Operator

Maybe the best example:

List<String> strings = new ArrayList<>();
  • used at a constructor call

  • tells Java to infer the parametric type

Anonymous Classes

Diamond did not work with anon. classes:

<T> Box<T> createBox(T content) {
	// we have to put the `T` here :(
	return new Box<T>(content) { };
}

Reason are non-denotable types:

  • might be inferred by compiler for anon. classes

  • can not be expressed by JVM

Infer Denotable Types

Java 9 infers denotable types:

<T> Box<T> createBox(T content) {
	return new Box<>(content) { };
}

Gives compile error if type is non-denotable:

Box<?> createCrazyBox(Object content) {
	List<?> innerList = Arrays.asList(content);
	// compile error
	return new Box<>(innerList) { };
}

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

One less warning you couldn’t do anything about.

Heap Pollution

Innocent looking code…​

private <T> Optional<T> firstNonNull(T... args) {
	return stream(args)
			.filter(Objects::nonNull)
			.findFirst();
}

Compiler warns (on call site, too):

Possible heap pollution from parameterized vararg type

Heap Pollution?

For generic varargs argument T…​ args,
you must not depend on it being a T[]!

private <T> T[] replaceTwoNulls(
		T value, T first, T second) {
	return replaceAllNulls(value, first, second);
}

private <T> T[] replaceAllNulls(T value, T... args) {
	// loop over `args`, replacing null with `value`
	return args;
}

Compiler Warning

Compiler is aware of the problem and warns you.

If you think, everything’s under control:

@SafeVarargs
private <T> Optional<T> firstNonNull(T... args) {
	return // [...]
}

Or not…​ In Java 8 this is a compile error!

Invalid SafeVarargs annotation. Instance
method <T>firstNonNull(T...) is not final.

But Why?

The @SafeVarargs annotation:

  • tells the caller that all is fine

  • only makes sense on methods
    that can not be overriden

Which methods can’t be overriden?
final methods

What about private methods?
⇝ Damn! 😭

@SafeVarargs on Private Methods

Looong story, here’s the point:

In Java 9 @SafeVarargs
can be applied to private methods.

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

Another warning you couldn’t do anything about.

Deprecation Warnings

Should this code emit a warning?

// LineNumberInputStream is deprecated
import java.io.LineNumberInputStream;


public class DeprecatedImports {

    LineNumberInputStream stream;

}
// LineNumberInputStream is deprecated
import java.io.LineNumberInputStream;

@Deprecated
public class DeprecatedImports {

    LineNumberInputStream stream;

}

Not On Imports

Java 9 no longer emits warnings
for importing deprecated members.

Warning free:

import java.io.LineNumberInputStream;

@Deprecated
public class DeprecatedImports {

	LineNumberInputStream stream;

}

New or Updated APIs

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

Some APIs are new,
many existing ones were improved.

Stream Improvements

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

Small improvements to Java 8 streams.

Stream::ofNullable

Creates a stream of zero or one elements:

long zero = Stream.ofNullable(null).count();
long one = Stream.ofNullable("42").count();

Stream::iterate

To use for even less…​

iterate(T seed,
	Predicate<T> hasNext,
	UnaryOperator<T> next);

Example:

Stream
	.iterate(1, i -> i<=10, i -> 2*i)
	.forEach(System.out::println);
// output: 1 2 4 8

Stream::iterate

Counter Example:

Enumeration<Integer> en = // ...
Stream.iterate(
		en.nextElement(),
		el -> en.hasMoreElements(),
		el -> en.nextElement())
	.forEach(System.out::println);
  • first nextElement()

  • then hasMoreElements()

  • ⇝ fail

Stream::takeWhile

Stream as long as a condition is true:

Stream<T> takeWhile(Predicate<T> predicate);

Example:

Stream.of("a", "b", "c", "", "e")
	.takeWhile(s -> !s.isEmpty());
	.forEach(System.out::print);

// output: abc

Stream::dropWhile

Start streaming as soon as a condition is true:

Stream<T> dropWhile(Predicate<T> predicate);

Example:

Stream.of("a", "b", "c", "de", "f")
	.dropWhile(s -> s.length() <= 1);
	.forEach(System.out::print);

// output: def

Optional Improvements

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

Small improvements to Java 8 Optional.

Optional::stream

Turns an Optional into a Stream
of zero or one elements:

Stream<T> stream();

Filter-Map in one Step

private Optional<Customer> findCustomer(int id) {
	// ...
}

Stream<Customer> findCustomers(List<String> ids) {
	return ids.stream()
		.map(this::findCustomer)
		// now we have a Stream<Optional<Customer>>
		.flatMap(Optional::stream)
}

From Eager to Lazy

List<Order> getOrders(String id) is expensive:

List<Order> findOrdersForCustomer(String id) {
	return findCustomer(id)
		.map(this::getOrders) // eager
		.orElse(new ArrayList<>());
}

Stream<Order> findOrdersForCustomer(String id) {
	return findCustomer(id)
		.stream()
		.map(this::getOrders) // lazy
		.flatMap(List::stream);
}

Optional::or

Choose a non-empty Optional:

Optional<T> or(Supplier<Optional<T>> supplier);

Find in Many Places

public interface Search {
	Optional<Customer> inMemory(String id);
	Optional<Customer> onDisk(String id);
	Optional<Customer> remotely(String id);

	default Optional<Customer> anywhere(String id) {
		return inMemory(id)
			.or(() -> onDisk(id))
			.or(() -> remotely(id));
	}

}

ifPresentOrElse

Like ifPresent but do something if empty:

void ifPresentOrElse(
	Consumer<T> action,
	Runnable emptyAction);

Example:

void logLogin(String id) {
	findCustomer(id)
		.ifPresentOrElse(
			this::logCustomerLogin,
			() -> logUnknownLogin(id));
}

Collection Factories

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

Creating ad-hoc collections more easily.

Hope is Pain

Wouldn’t this be awesome?

List<String> list = [ "a", "b", "c" ];
Map<String, Integer> map = [ "one" = 1, "two" = 2 ];

Not gonna happen!

  • language change is costly

  • binds language to collection framework

  • strongly favors specific collections

Next Best Thing

List<String> list = List.of("a", "b", "c");
Map<String, Integer> mapImmediate = Map.of(
		"one", 1,
		"two", 2,
		"three", 3);
Map<String, Integer> mapEntries = Map.ofEntries(
		entry("one", 1),
		entry("two", 2),
		entry("three", 3));

Interesting Details

  • collections are immutable
    (no immutability in type system, though)

  • null elements/keys/values are forbidden

  • iteration order is random between JVM starts
    (except for lists, of course!)

Reactive Streams

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

The JDK as common ground
for reactive stream libraries.

Reactive Types

Publisher
  • produces items to consume

  • can be subscribed to

Subscriber
  • subscribes to publisher

  • onNext, onError, onComplete

Subscription
  • connection between publisher and subscriber

  • request, cancel

Reactive Flow

Subscribing

  • create Publisher pub and Subscriber sub

  • call pub.subscribe(sub)

  • pub creates Subscription script
    and calls sub.onSubscription(script)

  • sub can store script

Reactive Flow

Streaming

  • sub calls script.request(10)

  • pub calls sub.onNext(element) (max 10x)

Canceling

  • pub may call sub.OnError(err)
    or sub.onComplete()

  • sub may call script.cancel()

Reactive APIs?

JDK only provides three interfaces
and one simple implementation.

(Also called Flow API.)

No JDK API uses them.
(No reactive HTTP connections etc.)

Stack-Walking

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

Examining the stack faster and easier.

StackWalker::forEach

public static void main(String[] args) { one(); }
static void one() { two(); }
static void two() { three(); }
static void three() {
	StackWalker.getInstance()
		.forEach(System.out::println);
}

// output
StackWalkingExample.three(StackWalking.java:15)
StackWalkingExample.two(StackWalking.java:12)
StackWalkingExample.one(StackWalking.java:11)
StackWalkingExample.main(StackWalking.java:10)

StackWalker::walk

T walk (Function<Stream<StackFrame>, T>);
static void three() {
	String line = StackWalker.getInstance().walk(
		frames -> frames
			.filter(f -> f.getMethodName().contains("one"))
			.findFirst()
			.map(f -> "Line " + f.getLineNumber())
			.orElse("Unknown line");
	);
	System.out.println(line);
}

// output
Line 11

Options

getInstance takes options as arguments:

  • SHOW_REFLECT_FRAMES for reflection frames

  • SHOW_HIDDEN_FRAMES e.g. for lambda frames

  • RETAIN_CLASS_REFERENCE for Class<?>

Frames and Traces

forEach and walk operate on StackFrame:

  • class and method name

  • class as Class<?>

  • bytecode index and isNative

Can upgrade to StackTraceElement (expensive):

  • file name and line number

Performance I

stack walker vs exception

Performance II

stack walker limit with estimated size

Performance III

  • creating StackTraceElement is expensive
    (for file name and line number)

  • lazy evaluation pays off for partial traversal

(Benchmarks performed by Arnaud Roger)

OS Processes

Stream Improvements
Optional Improvements
Collection Factories
Reactive Streams
Stack-Walking
OS Processes

Improving interaction with OS processes.

Simple Example

ls /home/nipa/tmp | grep pdf
Path dir = Paths.get("/home/nipa/tmp");
ProcessBuilder ls = new ProcessBuilder()
		.command("ls")
		.directory(dir.toFile());
ProcessBuilder grepPdf = new ProcessBuilder()
		.command("grep", "pdf")
		.redirectOutput(Redirect.INHERIT);
List<Process> lsThenGrep = ProcessBuilder
		.startPipeline(asList(ls, grepPdf));

Extended Process

Cool new methods on Process:

  • boolean supportsNormalTermination();

  • long pid();

  • CompletableFuture<Process> onExit();

  • Stream<ProcessHandle> children();

  • Stream<ProcessHandle> descendants();

  • ProcessHandle toHandle();

New ProcessHandle

New functionality actually comes from ProcessHandle.

Interesting static methods:

  • Stream<ProcessHandle> allProcesses();

  • Optional<ProcessHandle> of(long pid);

  • ProcessHandle current();

More Information

ProcessHandle can return Info:

  • command, arguments

  • start time

  • CPU time

A Mixed Bag Of API Changes

Many lower-level APIs.

JVM Features

Multi-Release JARs
Redirected Platform Logging

Multi-Release JARs

Multi-Release JARs
Redirected Platform Logging

"Do this on Java X, do that on Java Y."

Version Dependence

Main calls Version:

public class Main {

	public static void main(String[] args) {
		System.out.println(new Version().get());
	}

}

Version Dependence

Version exists twice:

public class Version {

	public String get() { return "Java 8"; }

}

public class Version {

	public String get() { return "Java 9"; }

}

(Btw, IDEs hate this!)

Creating A Multi‑Release JAR

Now, here’s the magic:

  • compile Main and Version[8] to out/java-8

  • compile Version[9] to out/java-9

  • use new jar flag --release:

    jar --create --file out/mr.jar
    	-C out/java-8 .
    	--release 9 -C out/java-9 .

JAR Content

└ org
    └ codefx ... (moar folders)
        ├ Main.class
        └ Version.class
└ META-INF
    └ versions
        └ 9
            └ org
                └ codefx ... (moar folders)
                    └ Version.class

Run!

With java -cp out/mr.jar …​Main:

  • prints "Java 8" on Java 8

  • prints "Java 9" on Java 9

Great Success!

Redirected Platform Logging

Multi-Release JARs
Redirected Platform Logging

Use your logging framework of choice
as backend for JDK logging.

Loggers and Finders

New logging infrastructure inside the JDK:

  • new interface System.Logger

  • used by JDK classes

  • instances created by System.LoggerFinder

The interesting bit:

LoggerFinder is a service!

Creating a Logger

public class SystemOutLogger implements Logger {

	public String getName() { return "SystemOut"; }

	public boolean isLoggable(Level level) { return true; }

	public void log(
			Level level, ResourceBundle bundle,
			String format, Object... params) {
		System.out.println(/* ...*/);
	}

	// another, similar `log` method

}

Creating a LoggerFinder

public class SystemOutLoggerFinder
		extends LoggerFinder {

	public Logger getLogger(
			String name, Module module) {
		return new SystemOutLogger();
	}

}

Registering the Service

Module descriptor for system.out.logger:

module system.out.logger {
    provides java.lang.System.LoggerFinder
        with system.out.logger.SystemOutLoggerFinder;
}

Module system and JDK take care of the rest!

A Mixed Bag Of New JVM Features

Performance

Compact Strings
Indified String Concatenation

Compact Strings

Compact Strings
Indified String Concatenation

Going from UTF-16 to ISO-8859-1.

Strings and memory

  • 20% - 30% of heap are char[] for String

  • a char is UTF-16 code unit ⇝ 2 bytes

  • most strings only require ISO-8859-1 ⇝ 1 byte

10% - 15% of memory is wasted!

Compact Strings

For Java 9, String was changed:

  • uses byte[] instead of char[]

  • bytes per character:

    • 1 if all characters are ISO-8859-1

    • 2 otherwise

Only possible because String makes
defensive copies of all arguments.

Performance

Simple benchmark:
(by Aleksey Shipilëv)

String method = generateString(size);

public String work() {
	return "Calling method \"" + method + "\"";
}

Depending on circumstances:

  • throughput 1.4x

  • garbage less 1.85x

Indified String Concatenation

Compact Strings
Indified String Concatenation

"Improving" + "String" + "Concatenation"

String Concatenation

What happens when you run:

String s = greeting + ", " + place + "!";
  • bytecode uses StringBuilder

  • JIT may (!) recognize and optimize
    by writing content directly to new char[]

  • breaks down quickly
    (e.g. with long or double)

Why Not Create Better Bytecode?

  • new optimizations create new bytecode

  • new optimizations require recompile

  • test matrix JVMs vs bytecodes explodes

Why Not Call String::concat?

There is no such method.

  • concat(String…​ args) requires toString

  • concat(Object…​ args) requires boxing

Nothing fancy can be done
because compiler must use public API.

Invokedynamic

Invokedynamic came in Java 7:

  • compiler creates a recipe

  • runtime has to process it

  • defers decisions from compiler to runtime

(Used for lambda expressions and in Nashorn.)

Indy To The Rescue

With Indy compiler can express
"concat these things"
(without boxing!)

JVM executes by writing content
directly to new char[].

Performance

Depending on circumstances:

  • throughput 2.6x

  • garbage less 3.4x

(Benchmarks by Aleksey Shipilëv)

Performance Of Indified Compact String Concat

Depending on circumstances:

  • throughput 2.9x

  • garbage less 6.4x

(Benchmarks by Aleksey Shipilëv)

A Mixed Bag Of Performance Improvements

Full Picture

If you want the full picture, read

The Ultimate Guide To Java 9

About Nicolai Parlog

37% off with
code fccparlog

tiny.cc/j9ms

Want More?

⇜ Get my book!

I write a mean newsletter,
currently mostly about Java 9.

You can hire me.

Image Credits