Java 11 Is Here

Let’s get started!

Lots to talk about!

Language Changes
New and Updated APIs
New JVM Features
Performance Improvements

New Language Features

Local-Variable Type Inference ⑩
Private Interface Methods ⑨
Try-With-Resources ⑨
Diamond Operator ⑨
SafeVarargs ⑨
Deprecation Warnings ⑨

Sneak Preview

Java 12 switch expressions:

public void switchExpression() {
	var bool = Bool.random();
	boolean result = switch (bool) {
		case TRUE -> true;
		case FALSE -> false;
		case FILE_NOT_FOUND ->
			throw new UncheckedIOException(
				"This is ridiculous!",
				new FileNotFoundException());
	};
}

New Language Features

Local-Variable Type Inference ⑩
Private Interface Methods ⑨
Try-With-Resources ⑨
Diamond Operator ⑨
SafeVarargs ⑨
Deprecation Warnings ⑨

Type inference with var.
Less typing, but still strongly typed.

Type Duplication

We’re used to duplicating
type information:

URL codefx = new URL("http://codefx.org");
URLConnection connection = codefx.openConnection();
Reader reader = new BufferedReader(
	new InputStreamReader(
		connection.getInputStream()));

Not so bad?

Type Duplication

What about this?

No no = new No();
AmountIncrease<BigDecimal> more =
	new BigDecimalAmountIncrease();
HorizontalConnection<LinePosition, LinePosition>
	jumping =
		new HorizontalLinePositionConnection();
Variable variable = new Constant(5);
List<String> names = List.of("Max", "Maria");

Type Deduplication

Can’t somebody else do that?
Compiler knows the types!

Enter var:

var codefx = new URL("http://codefx.org");
var connection = codefx.openConnection();
var reader = new BufferedReader(
	new InputStreamReader(
		connection.getInputStream()));

Locality

How much information is used for inference?

  • type inference can be
    arbitrarily complex/powerful

  • critical resource is not
    compiler but developer

  • code should be readable
    (without compiler/IDE)

⇝ Better to keep it simple!

"Action at a distance"

// inferred as `int`
var id = 123;
if (id < 100) {
	// very long branch
} else {
	// oh boy, much more code...
}

// now we add this line:
id = "124";

What type should id be?

Where does the error show up?

Rules of var

Hence, var only works in limited scopes:

  • compiler infers type from right-hand side
    ⇝ rhs has to exist and define a type

  • only works for local variables, for, try
    ⇝ no var on fields or in method signatures

  • also on lambda parameters ⑪
    ⇝ annotate inferred type on lambda parameters

Rules of var

Two more:

  • not a keyword, but a reserved type name
    ⇝ variables/fields can be named var

  • compiler writes type into bytecode
    ⇝ no run-time component

What About Readability?

This is about readability!

  • less redundancy

  • more intermediate variables

  • more focus on variable names

  • aligned variable names

Aligned Variable Names

var no = new No();
var more = new BigDecimalAmountIncrease();
var jumping = new HorizontalLinePositionConnection();
var variable = new Constant(5);
var names = List.of("Max", "Maria");

What About Readability?

Still think omitting types is always bad?

Ever wrote a lambda without declaring types?

rhetoricalQuestion.answer(yes -> "see my point?");

Style Guidelines

Principles from the official style guidelines:

  1. Reading code is more important than writing it.

  2. Code should be clear from local reasoning.

  3. Code readability shouldn’t depend on IDEs.

  4. Explicit types are a tradeoff.

Style Guidelines

Guidelines:

  1. Choose variable names that provide useful info.

  2. Minimize the scope of local variables.

  3. Consider var when the initializer provides sufficient information to the reader.

  4. Use var to break up chained or nested expressions.

  5. Don’t worry too much about "programming to the interface".

  6. Take care when using var with diamonds or generics.

  7. Take care when using var with literals.

More on var

New Language Features

Local-Variable Type Inference ⑩
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 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

Local-Variable Type Inference ⑩
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

Local-Variable Type Inference ⑩
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 anonymous 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 anonymous 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

Local-Variable Type Inference ⑩
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

Local-Variable Type Inference ⑩
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;

}

Updated APIs

String ⑪
Stream ⑨⑩
Optional ⑨⑩⑪
OS Processes ⑨

Existing APIs are continuously improved.

Updated APIs

String ⑪
Stream ⑨⑩
Optional ⑨⑩⑪
OS Processes ⑨

Small improvements to String.

Strip White Space

Getting rid of white space:

String strip();
String stripLeading();
String stripTrailing();

Only at beginning and end of string:

" foo bar ".strip().equals("foo bar");

What About Trim?

Wait, what about trim()?

  • trim() defines white space as:

    any character whose codepoint
    is less than or equal to 'U+0020'
    (the space character)

  • strip() relies on Character::isWhitespace,
    which covers many more cases

Is Blank?

Is a string only white space?

boolean isBlank();

Functionally equivalent to:

string.isBlank() == string.strip().isEmpty();

Life Hack

As soon as Java APIs get new method,
scour StackOverflow for easy karma!

so repeat string q

Life Hack

Formerly accepted answer:

so repeat string a old

😍

Life Hack

Ta-da!

so repeat string a

Streaming Lines

Processing a string’s lines:

Stream<String> lines();
  • splits a string on "\n", "\r", "\r\n"

  • lines do not include terminator

  • more performant than split("\R")

  • lazy!

Updated APIs

String ⑪
Stream ⑨⑩
Optional ⑨⑩⑪
OS Processes ⑨

Small improvements to Java 8 streams.

Of Nullable ⑨

Create a stream of zero or one elements:

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

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

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

Take While ⑨

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: a-b-c-

Drop While ⑨

Ignore as long as a condition is true:

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

Example:

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

// output: de-f-

Collect Unmodifiable ⑩

Create unmodifiable collections
(in the sense of List::of et al)
with Collectors:

Collector<T, ?, List<T>> toUnmodifiableList();

Collector<T, ?, Set<T>> toUnmodifiableSet();

Collector<T, ?, Map<K,U>> toUnmodifiableMap(
	Function<T, K> keyMapper,
	Function<T, U> valueMapper);
// plus overload with merge function

Updated APIs

String ⑪
Stream ⑨⑩
Optional ⑨⑩⑪
OS Processes ⑨

Small improvements to Java 8 Optional.

Is Empty ⑪

No more !foo.isPresent():

boolean isEmpty()

Does exactly what
you think it does.

Or Else Throw ⑩

Optional::get invites misuse
by calling it reflexively.

Maybe get wasn’t the best name?
New:

T orElseThrow()

Works exactly as get,
but more self-documenting.

Aligned Names

Name in line with other accessors:

T orElse(T other)
T orElseGet(Supplier<T> supplier)
T orElseThrow()
	throws NoSuchElementException
T orElseThrow(
	Supplier<X> exceptionSupplier)
	throws X

Get Considered Harmful

JDK-8160606 will deprecate
Optional::get.

  • when?

  • for removal?

We’ll see…​

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));
	}

}

If Present Or Else ⑨

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));
}

Stream ⑨

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

Stream<T> stream();

Filter-Map …​

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

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

…​ in one Step

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

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

From Eager to Lazy

List<Order> getOrders(Customer c) 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);
}

Updated APIs

String ⑪
Stream ⑨⑩
Optional ⑨⑩⑪
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(List.of(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 Updated APIs

In Java 9:

  • OASIS XML Catalogs 1.1 (JEP 268),
    Xerces 2.11.0 (JEP 255)

  • Unicode support in
    PropertyResourceBundle (JEP 226)

Many lower-level APIs.

A Mixed Bag Of New I/O Methods

In Java 10 & 11:

Path.of(String); // ~ Paths.get(String)

Files.readString(Path);
Files.writeString(Path, CharSequence, ...);

Reader.transferTo(Writer);
ByteArrayInputStream.transferTo(OutputStream);

Reader.nullReader();
Writer.nullWriter();
InputStream.nullInputStream();
OutputStream.nullOutputStream();

A Mixed Bag Of New Methods

In Java 10:

DateTimeFormatter.localizedBy(Locale locale);

In Java 11:

Collection.toArray(IntFunction<T[]> generator);
Predicate.not(Predicate<T> target); // static
Pattern.asMatchPredicate(); // ⇝ Predicate<String>

New APIs

Collection Factories ⑨⑩
Reactive Streams ⑨
Reactive HTTP/2 Client ⑪
Stack-Walking ⑨

New APIs are added over time.

New APIs

Collection Factories ⑨⑩
Reactive Streams ⑨
Reactive HTTP/2 Client ⑪
Stack-Walking ⑨

Easy creation of ad-hoc collections.

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)

  • collections are value-based

  • null elements/keys/values are forbidden

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

Immutable Copies ⑩

Creating immutable copies:

/* on List */ List<E> copyOf(Collection<E> coll);
/* on Set */ Set<E> copyOf(Collection<E> coll);
/* on Map */ Map<K, V> copyOf(Map<K,V> map);

Great for defensive copies:

public Customer(List<Order> orders) {
	this.orders = List.copyOf(orders);
}

New APIs

Collection Factories ⑨⑩
Reactive Streams ⑨
Reactive HTTP/2 Client ⑪
Stack-Walking ⑨

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.)

New APIs

Collection Factories ⑨⑩
Reactive Streams ⑨
Reactive HTTP/2 Client ⑪
Stack-Walking ⑨

HTTP/2! And reactive! Woot!

Basic Flow

To send a request and get a response:

  • use builder to create immutable HttpClient

  • use builder to create immutable HttpRequest

  • pass request to client to receive HttpResponse

Building a Client

HttpClient client = HttpClient.newBuilder()
	.version(HTTP_2)
	.connectTimeout(ofSeconds(5))
	.followRedirects(ALWAYS)
	.build();

More options:

  • proxy

  • SSL context/parameters

  • authenticator

  • cookie handler

Building a Request

HttpRequest request = HttpRequest.newBuilder()
	.GET()
	.uri(URI.create("http://codefx.org"))
	.setHeader("header-name", "header-value")
	.build();
  • more HTTP methods (duh!)

  • individual timeout

  • individual HTTP version

  • request "100 CONTINUE" before sending body

Receiving a Response

// the generic `String`...
HttpResponse<String> response = client.send(
	request,
	// ... comes from this body handler ...
	BodyHandlers.ofString());
// ... and defines `body()`s return type
String body = response.body();
  • status code, headers, SSL session

  • request

  • intermediate responses
    (redirection, authentication)

Reactive?

Great, but where’s the reactive sauce?

Three places:

  • send request asynchronously

  • provide request body with
    Publisher<ByteBuffer>

  • receive response body with
    Subscriber<String> or
    Subscriber<List<ByteBuffer>>

Asynchronous Request

Submit request to thread pool until completes:

CompletableFuture<String> responseBody = client
	.sendAsync(request, BodyHandlers.ofString())
	.thenApply(this::logHeaders)
	.thenApply(HttpResponse::body);
  • uses "a default executor" to field requests

  • pool can be defined when client is built with
    HttpClient.Builder.executor(Executor)

Reactive Request Body

If a request has a long body,
no need to prepare it in its entirety:

Publisher<ByteBuffer> body = // ...
HttpRequest post = HttpRequest.newBuilder()
	.POST(BodyPublishers.fromPublisher(body))
	.build();
client.send(post, BodyHandlers.ofString())
  • client subscribes to body

  • as body publishes byte buffers,
    client sends them over the wire

Reactive Response Body

If a response has a long body,
no need to wait before processing:

Subscriber<String> body = // ...
HttpResponse<Void> response = client.send(
	request,
	BodyHandlers.fromLineSubscriber(subscriber));
  • client subscribes body to itself

  • as client receives response bytes,
    it parses to lines and passes to body

Reactive Benefits

Benefits of reactive
request/response bodies:

  • receiver applies backpressure:

    • on requests, client

    • on responses, body

  • body controls memory usage

  • early errors lead to partial processing

  • need "reactive tools" to create body
    from higher-level Java objects (e.g. File)

Web Sockets

Short version:

  • there’s a class WebSocket

  • send[Text|Binary|…​] methods
    return CompletableFuture

  • socket calls Listener methods
    on[Text|Binary|…​]

(WebSocket and Listener behave like
Subscription and Subscriber.)

No long version. 😛

New APIs

Collection Factories ⑨⑩
Reactive Streams ⑨
Reactive HTTP/2 Client ⑪
Stack-Walking ⑨

Examining the stack faster and easier.

StackWalker::forEach

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

// output
StackWalkingExample.two(StackWalking.java:14)
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)

A Mixed Bag Of New APIs

In Java 9:

Many lower-level APIs.

JVM Features

Launch Source File ⑪
Unified Logging ⑨
Multi-Release JARs ⑨
Redirected Platform Logging ⑨

JVM Features

Launch Source File ⑪
Unified Logging ⑨
Multi-Release JARs ⑨
Redirected Platform Logging ⑨

Faster feedback with fewer tools.

Launching A Single Source File

Compiling and running
simple Java programs is verbose.

Not any more!

java HelloJava11.java

Background

How it works:

  • compiles source into memory

  • runs from there

Details:

  • requires module jdk.compiler

  • processes options like class/module path et al.

  • interprets @files for easier option management

Use Cases

Mostly similar to jshell:

  • easier demonstrations

  • more portable examples

  • experimentation with new language features
    (combine with --enable-preview)

But also: script files!

Scripts

Steps towards easier scripting:

  • arbitrary file names

  • shebang support

Arbitrary File Names

Use --source if file doesn’t end in .java:

java --source 11 hello-java-11

Shebang Support

To create "proper scripts":

  • include shebang in source:

    #!/opt/jdk-11/bin/java --source 11
  • name script and make it executable

  • execute it as any other script:

    # from current directory:
    ./hello-java-11
    # from PATH:
    hello-java-11

JVM Features

Launch Source File ⑪
Unified Logging ⑨
Multi-Release JARs ⑨
Redirected Platform Logging ⑨

Observing the JVM at work.

Unified Logging

New logging infrastructure for the JVM
(e.g. OS interaction, threading, GC, etc.):

  • JVM log messages pass through new mechanism

  • works similar to known logging frameworks:

    • textual messages

    • log level

    • time stamps

    • meta information (like subsystem)

  • output can be configured with -Xlog

Unified Logging

unified logging

First Try

Plain use of -Xlog:

$ java -Xlog -version

# truncated a few messages
> [0.002s][info][os       ] HotSpot is running ...
# truncated a lot of messages

You can see:

  • JVM uptime (2ms)

  • log level (info)

  • tags (os)

  • message

Configuring -Xlog

This can be configured:

  • which messages to show

  • where messages go

  • what messages should say

How? -Xlog:help lists all options.

Which Messages?

Configure with selectors: $TAG_SET=$LEVEL:

# "exactly gc" on "warning"
-Xlog:gc=warning
# "including gc" on "warning"
-Xlog:gc*=warning
# "exactly gc and os" on "debug"
-Xlog:gc+os=debug
# "gc" on "debug" and "os" on warning
-Xlog:gc=debug,os=warning

Defaults:

-Xlog       # the same as -Xlog:all
-Xlog:$TAG  # same as -Xlog:$TAG=info

Where Do Messages Go?

Three possible locations:

  • stdout (default)

  • stderr

  • file=$FILENAME
    (file rotation is possible)

Example:

# all debug messages into application.log
-Xlog:all=debug:file=application.log

Which Information?

Decorators define what is shown:

  • time: time and date (also in ms and ns)

  • uptime: time since JVM start (also in ms and ns)

  • pid: process identifier

  • tid: thread identifier

  • level: log level

  • tags: tag-set

Example:

# show uptime in ms and level
-Xlog:all:stdout:level,uptimemillis

Put Together

Formal syntax:

-Xlog:$SELECTORS:$OUTPUT:$DECORATORS:$OUTPUT_OPTS
  • $SELECTORS are pairs of tag sets and log levels
    (the docs call this what-expression)

  • $OUTPUT is stdout, stderr, or file=<filename>

  • $DECORATORS define what is shown

  • $OUTPUT_OPTS configure file rotation

Elements have to be defined from left to right.
(No skipping!)

JVM Features

Launch Source File ⑪
Unified Logging ⑨
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!

JVM Features

Launch Source File ⑪
Unified Logging ⑨
Multi-Release JARs ⑨
Redirected Platform Logging ⑨

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

Loggers and Finders

New logging infrastructure for the core libraries
(i.e. this does not apply to JVM log messages!)

  • 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

In Java 9:

A Mixed Bag Of New JVM Features

In Java 10:

  • alternative memory device support (JEP 316)

In Java 11:

Performance

Application Class-Data Sharing ⑩
Compact Strings ⑨
Indified String Concatenation ⑨

Performance

Application Class-Data Sharing ⑩
Compact Strings ⑨
Indified String Concatenation ⑨

Improving application launch times.

Class-Data

JVM steps to execute a class’s bytecode:

  • looks up class in JAR

  • loads bytecode

  • verifies bytecode

  • stores class-data in
    internal data structure

This takes quite some time.

If classes don’t change, the resulting
class-data is always the same!

Class-Data Sharing

Idea behind class-data sharing:

  • create class-data once

  • dump it into an archive

  • reuse the archive in future launches
    (file is mapped into memory)

Effects

My experiments with a large desktop app
(focusing on classes required for launch):

  • archive has 250 MB for ~24k classes

  • launch time reduced from 15s to 12s

Bonus: Archive can be shared across JVMs.

Class-Data Sharing

Two variants:

CDS

Just for JDK classes
(⑫ includes archive — JEP 341)

AppCDS

JDK + application classes

We will focus on AppCDS.

AppCDS - Step #1

Create a list of classes

$ java
	-XX:DumpLoadedClassList=classes.lst
	# [... class path for dependencies ...]
	-jar app.jar

Then, classes.lst contains
slash-separated names of loaded classes.

AppCDS - Step #2

Use the list to create the archive:

$ java
	-Xshare:dump
	-XX:SharedClassListFile=classes.lst
	-XX:SharedArchiveFile=app-cds.jsa
	--class-path app.jar # plus dependencies

Creates archive app-cds.jsa.

AppCDS - Step #3

Use the archive:

$ java
	-Xshare:on
	-XX:SharedArchiveFile=app-cds.jsa
	# class path for dependencies
	-jar app.jar

Heed The Class Path

What are the two biggest challenges
in software development?

  1. naming

  2. cache invalidation

  3. off-by-one errors

Heed The Class Path

The archive is a cache!

It’s invalid when:

  • a JAR is updated

  • class path is reordered

  • a JAR is added
    (unless when appended)

Heed The Class Path

To invalidate the archive:

  • during creation:

    • Java stores used class path in archive

    • class path may not contain wild cards

    • class path may not contain exploded JARs

  • when used:

    • Java checks whether stored path
      is prefix of current path

Module Path?

Class path, class path…​
what about the module path?

In this release, CDS cannot archive classes from user-defined modules (such as those specified in --module-path). We plan to add that support in a future release.

More On (App)CDS

For more, read this article:
tiny.cc/app-cds

Observe sharing with
-Xlog:class+load
(unified logging)

Performance

Application Class-Data Sharing ⑩
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

Performance

Application Class-Data Sharing ⑩
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 byte[]

  • 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 byte[].

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

In Java 9:

A Mixed Bag Of Performance

In Java 10:

  • Graal — Java-based JIT compiler (JEP 317)

  • parallel full GC for G1 (JEP 307)

  • thread-local handshakes (JEP 312)

In Java 11:

Full Picture

If you want the full picture:

All You Need To Know For
Migrating To Java 11

tiny.cc/java-11

Code First Java 9 Tutorial
tiny.cc/java-9-tutorial

About Nicolai Parlog

37% off with
code fccparlog

tiny.cc/jms

Follow

Want More?

⇜ Get my book!

You can hire me:

  • training (Java 8-11, JUnit 5)

  • consulting (Java 8-11)

Image Credits