Java 9 performance optimizations

Java 9 performance optimizations

In a previous article on Java 9, I listed all the main new features for the developers : https://www.loicmathieu.fr/wordpress/en/informatique/les-nouveautes-de-java-9-pour-les-developeurs.

Here, I will list all the main performance optimizations of Java 9.

I will again go through the main JEP :

JEP 143: Improve Contended Locking

Optimization of Java monitors (lock optimization) when contended (when multiple threads access the same monitor). These are changes inside the JVM that allow siginficant (x2) performance boost on some JVM operations on lock primitives on objects (synchronize, wait, …).

More information in the following presentation by Monica Beckwith :

https://www.slideshare.net/MonicaBeckwith/jfokus-java-9-contended-locking-performance

JEP 193: Variable Handles

A new API in Java, targeting advanced users, allowing to replace the usage of Unsafe for atomically accessing in a performant way variables.

A variable handle is a typed reference to a variable, which supports read and write access to the variable under a variety of access modes. Supported variable kinds include instance fields, static fields and array elements.

More information in the description of the JEP : http://openjdk.java.net/jeps/193

JEP 197: Segmented Code Cache

The code cache (a part of the Metaspace) has been separated by code types (method, non-method, profiled, non-profiled) for performance optimization and allowing future optimizations.

More information in the description of the JEP : http://openjdk.java.net/jeps/197

JEP 254: Compact Strings

That’s one of the main JEP around performance!

The java String implementation was reworked to allow a more compact version.

Until now, Strings was stored in UTF-16, each character took 2 bytes as stored in an array of char.

With Compact String, at the String construction time, if the String is ISO-8859-1 (or Latin-1) compatible, each character will then be stored in only one byte, the String storage evolves from an array of char to an array of bytes. The potential gain is huge : almost 50% shrinking of the size needed to store Strings. Strings are usually the main source of memory usage and generally they are ISO-8859-1 compatible.

If the String is not ISO-8859-1 compatible, then characters are still stored in a bytes array, but you will need two bytes for each one. A flag was added to the String class to know the encoding of this one.

This change has been made without any changes on the public API of Java : by using Java 9, you directly use Compact Strings! StringBuilder/Buffer was also updated to take advantage of it.

During preliminary tests for the implementation of this JEP, the following gains have been seen (see http://cr.openjdk.java.net/~shade/density/state-of-string-density-v1.txt) :

  • Gain of 5% to 15% of the size of the heap (25% of objects are String)
  • Performance gain of 20% for Latin-1 compared to UTF-16 (30% less garbage) : microbenchmark of String instanciation/concatenation

Moreover, this change open the door to some more optimizations in the code of Java and the JVM (like String encoding/decoding).

More information in the description of the JEP : http://openjdk.java.net/jeps/254

JEP 274: Enhanced Method Handles

Multiple addons to the MethodHandle API that allows to create new compiler optimizations.

More information in the description of the JEP : http://openjdk.java.net/jeps/274

JEP 280: Indify String Concatenation

That’s one of the main JEP around performance!

After the String compaction, introducing the String optimized concatenation!

First, some explanations.

When we concatenate strings in Java, we create a lot of ephemeral objects, that will then be collected by the Garbage Collector (GC):

String s = "toto" + "tata" + "titi" + "tutu"

This leads to the creation of 6 strings as strings are immutable in Java : “toto”, “tata”, “titi”,”tototata”, “tototatatiti”, “tototatatititutu” … and in the end we will only keep s and the other objects will be cleaned by the GC.

Since java 6, the Java compiler (javac) try to optimize this by replacing, at compile time, these concatenations by the use of StringBuilder. It replaces the previous code by

String s = new StringBuilder("toto").append("tata").append("titi").append("tutu").toString();

This optimization, although effective, has some limits, among others it depends of the capacity of the compiler to detect String concatenation. Moreover, the Java Just In Time Compiler (JIT) also optimize these String concatenations in a multiple of levels.

The JEP 280 replaces all this by the usage of the InvokeDynamic bytecode for the String concatenation and implement this at runtime by a concatenation strategy optimized directly inside the JVM (an intrinsic). Multiple string concatenation strategies has been developed, including one based on MethodHandles (with inline StringBuilder), this is the default one.

Tests made while developing this functionality shown that (see http://cr.openjdk.java.net/~shade/8085796/notes.txt):

  • Performances when the code is compiled by C2 (the last level of the JIT) are the same
  • Performances when the code is only compiled by C1 (le first level of the JIT) are better
  • Performances when C1/C2 didn’t succeed of optimizing the string concatenation are way beter !

In conclusion : no performance regression, better starting performances (when the code is not compiled or only by C1), better performance when javac or C1/C2 didn’t succeed in optimizing the code!

More information in the description of the JEP : http://openjdk.java.net/jeps/280

JEP 285: Spin-Wait Hints

A new method in the Thread class allow to notify the JVM that we are in a spin-wait loop. The JVM is then free to do or not do something about it (this is a native method). A spin-wait loop is a loop in which the Thread that execute it cannot make anything because it is waiting on an external event (busy wait).

Here are an exemple from the JavaDoc :

class EventHandler {
  volatile boolean eventNotificationNotReceived;
  
  void waitForEventAndHandleIt() {
    while ( eventNotificationNotReceived ) {
      java.lang.Thread.onSpinWait();
    }
  readAndProcessEvent();
}

  void readAndProcessEvent() {
    // Read event from some source and process it
    . . .
  }
}

More information in the description of the JEP : http://openjdk.java.net/jeps/285

2 thoughts on “Java 9 performance optimizations

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.