Slide 1

Slide 1 text

Performance by Dmitry Vyazelenko @ Oracle Code One 2018

Slide 2

Slide 2 text

About me • Senior Software Engineer @ Canoo Engineering AG, Switzerland • Disorganizer at JCrete (jcrete.org) and Jalba (jalba.scot) Unconferences • Contacts: vyazelenko.com, @DVyazelenko

Slide 3

Slide 3 text

Agenda ● Modular JDK: startup and footprint ● JEP 243: Java-Level JVM Compiler Interface ● JEP 248: Make G1 the Default Garbage Collector ● JEP 254: Compact Strings ● JEP 280: Indify String Concatenation ● JEP 269: Convenience Factory Methods for Collections ● JEP 259: Stack-Walking API ● New APIs

Slide 4

Slide 4 text

Modular JDK: startup and footprint

Slide 5

Slide 5 text

Footprint Download size, MB Installed size, MB jdk1.8.0_162 166 379 jdk-11+28 186 312

Slide 6

Slide 6 text

package com.vyazelenko.hello; public class Hello { public static void main(String[] args) { System.out.println("Hello, world!"); } }

Slide 7

Slide 7 text

JDK 11 JDK 8 62 69 54 58 JVM startup time -jar -cp Time, ms perf stat -r50 ...

Slide 8

Slide 8 text

JDK 11 JDK 8 76 78 62 69 54 58 JVM startup time -p modlibs --module-path -jar -cp Time, ms

Slide 9

Slide 9 text

JDK 11 JDK 8 56 76 78 62 69 54 58 JVM startup time jlink -modlibs -modpath -jar -cp Time, ms

Slide 10

Slide 10 text

JDK 11 JDK 8 42 56 73 78 72 69 54 58 JVM startup time AOT jlink -modlibs -modpath -jar -cp Time, ms

Slide 11

Slide 11 text

JDK 11 JDK 8 42 56 73 78 62 69 54 58 49 39 JVM startup time AOT jlink -p modlibs --module-path -jar -cp CDS* Time, ms

Slide 12

Slide 12 text

JEP 243: Java-Level JVM Compiler Interface

Slide 13

Slide 13 text

Summary Develop a Java based JVM compiler interface (JVMCI) enabling a compiler written in Java to be used by the JVM as a dynamic compiler. Goals ● Allow a Java component programmed against the JVMCI to be loaded at runtime and used by the JVM’s compile broker. ● Allow a Java component programmed against the JVMCI to be loaded at runtime and used by trusted Java code to install machine code in the JVM that can be called via a Java reference to the installed code.

Slide 14

Slide 14 text

JEP 248: Make G1 the Default Garbage Collector

Slide 15

Slide 15 text

● Default GC changed in JDK 9 ● See [3] Why you want to run Java 10 if you're using the G1 Garbage Collector ● Here be dragons (see [2] What a difference a JVM makes? )

Slide 16

Slide 16 text

JEP 254: Compact Strings

Slide 17

Slide 17 text

import org.openjdk.jol.info.ClassLayout; import org.openjdk.jol.info.GraphLayout; import org.openjdk.jol.vm.VM; import static java.lang.System.out; public class StringLayout { public static void main(String[] args) { String value = "Lorem ipsum ... est laborum."; out.println(VM.current().details()); out.println(ClassLayout.parseClass(String.class).toPrintable()); out.println(GraphLayout.parseInstance(value).toFootprint()); } }

Slide 18

Slide 18 text

JDK 8 java.lang.String object internals: OFFSET SIZE TYPE DESCRIPTION 0 12 (object header) 12 4 char[] String.value 16 4 int String.hash 20 4 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total Total size in bytes: 936

Slide 19

Slide 19 text

JDK 11 java.lang.String object internals: OFFSET SIZE TYPE DESCRIPTION 0 12 (object header) 12 4 byte[] String.value 16 4 int String.hash 20 1 byte String.coder 21 3 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 3 bytes external = 3 bytes total Total size in bytes: 488

Slide 20

Slide 20 text

Benchmark JDK 8 JDK 11 JDK 11 -XX:-CompressedStrings StringBenchmark.charAt_Latin1 65 ns/op 67 ns/op 62 ns/op StringBenchmark.charAt_UTF16 64 ns/op 65 ns/op 63 ns/op StringBenchmark.toLowerCase_Latin1 54 ns/op 120 B/op 29 ns/op 56 B/op 47 ns/op 120 B/op StringBenchmark.toLowerCase_UTF16 114 ns/op 120 B/op 108 ns/op 176 B/op 119 ns/op 144 B/op StringBenchmark.equals_Latin1_Latin1 5 ns/op 8 ns/op 6 ns/op StringBenchmark.equals_Latin1_UTF16 5 ns/op 3 ns/op 6 ns/op

Slide 21

Slide 21 text

public class StringMaxSize { public static void main(String[] args) { char[] data = new char[Integer.MAX_VALUE - 2]; data[0] = '\uD83D'; data[1] = '\uDC7B'; String value = new String(data); System.out.println(value.hashCode()); } } Exception in thread "main" java.lang.OutOfMemoryError: UTF16 String size is 2147483645, should be less than 1073741823 at java.base/java.lang.StringUTF16.newBytesFor(StringUTF16.java:46) at java.base/java.lang.StringUTF16.toBytes(StringUTF16.java:148) at java.base/java.lang.String.(String.java:3023) at java.base/java.lang.String.(String.java:249) at com.vyazelenko.perf.string.StringMaxSize.main(StringMaxSize.java:9)

Slide 22

Slide 22 text

JEP 280: Indify String Concatenation

Slide 23

Slide 23 text

import static java.lang.System.currentTimeMillis; import static java.lang.System.out; public class StringConcatExplore { public static void main(String[] args) { log("msg", 42); } private static void log(String msg, int count) { out.println("[" + currentTimeMillis() + "]: " + msg + " (" + count + ")"); } }

Slide 24

Slide 24 text

JDK 8 0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #5 // class java/lang/StringBuilder 6: dup 7: invokespecial #6 // Method java/lang/StringBuilder."":()V 10: ldc #7 // String [ 12: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: invokestatic #9 // Method java/lang/System.currentTimeMillis:()J 18: invokevirtual #10 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 21: ldc #11 // String ]: 23: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: aload_0 27: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: ldc #12 // String ( 32: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: iload_1 36: invokevirtual #13 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 39: ldc #14 // String ) 41: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 44: invokevirtual #15 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 47: invokevirtual #16 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 50: return

Slide 25

Slide 25 text

JDK 11 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: invokestatic #8 // Method java/lang/System.currentTimeMillis:()J 6: aload_0 7: iload_1 8: invokedynamic #9, 0 // InvokeDynamic #0:makeConcatWithConstants:(JLjava/lang/String;I)Ljava/lang/String; 13: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: return BootstrapMethods: 0: #44 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/ MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/ Object;)Ljava/lang/invoke/CallSite; Method arguments: #45 [\u0001]: \u0001 (\u0001)

Slide 26

Slide 26 text

String concat strategies ● Bytecode generator – BC_SB – empty StringBuilder – BC_SB_SIZED – pre-sized StringBuilder – BC_SB_SIZED_EXACT – exactly sized StringBuilder ● MethodHandle combinators – MH_SB_SIZED – pre-sized StringBuilder – MH_SB_SIZED_EXACT – exactly sized StringBuilder – MH_INLINE_SIZED_EXACT (default) – byte[] + exact sizing

Slide 27

Slide 27 text

StringConcatBenchmark JDK 8 142 ns/op 432 B/op JDK 11 50 ns/op 80 B/op JDK 11 -Djava.lang.invoke.stringConcat=BC_SB 107 ns/op 280 B/op JDK 11 -Djava.lang.invoke.stringConcat=BC_SB_SIZED 80 ns/op 176 B/op JDK 11 -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT 67 ns/op 160 B/op JDK 11 -Djava.lang.invoke.stringConcat=MH_SB_SIZED 79 ns/op 176 B/op JDK 11 -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT 88 ns/op 216 B/op

Slide 28

Slide 28 text

public class StringConcatLoopBenchmark { @Benchmark public String naive() { String result = ""; for (int i = 0; i < iterations; i++) { result += (i + " " + log() + "\n"); } return result; } @Benchmark public String proper() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < iterations; i++) { sb.append(i).append(" ").append(log()).append("\n"); } return sb.toString(); } }

Slide 29

Slide 29 text

Benchmark JDK 8 JDK 11 StringConcatLoopBenchmark.naive 48 µs/op 384288 B/op 8 µs/op 52880 B/op StringConcatLoopBenchmark.proper 9 µs/op 26496 B/op 4 µs/op 10328 B/op

Slide 30

Slide 30 text

JEP 269: Convenience Factory Methods for Collections

Slide 31

Slide 31 text

32 16 40 40 56 80 112 232 240 432 List.of() List.of() Collections.emptyList() List.of(1) Collections.singletonList(1) List.of(1,2) new ArrayList(1,2) new LinkedList(1,2) List.of(1..10) new ArrayList(1..10) new LinkedList(1..10) Footprint in bytes

Slide 32

Slide 32 text

32 16 40 40 56 104 136 232 264 456 List.of() vs Collections.unmodifiableList() List.of() Collections.emptyList() List.of(1) Collections.singletonList(1) List.of(1,2) new ArrayList(1,2) new LinkedList(1,2) List.of(1..10) new ArrayList(1..10) new LinkedList(1..10) Footprint in bytes

Slide 33

Slide 33 text

40 16 40 32 56 272 296 208 280 656 808 656 Set.of() vs Collections.unmodifiableSet() Set.of() Collections.emptySet() Set.of(1) Collections.singleton(1) Set.of(1,2) new HashSet(1,2) new LinkedHashSet(1,2) new TreeSet(1,2) Set.of(1..10) new HashSet(1..10) new LinkedHashSet(1..10) new TreeSet(1..10) Footprint in bytes

Slide 34

Slide 34 text

JEP 259: Stack-Walking API

Slide 35

Slide 35 text

public class ExceptionBenchmark { @Benchmark public Class> caller_class() throws Exception { return doCall(() -> { StackTraceElement[] stackTrace = new Exception().getStackTrace(); return Class.forName(stackTrace[2].getClassName()); }, stackDepth); } @Benchmark public int stack_depth() throws Exception { return doCall(() -> new Exception().getStackTrace().length, stackDepth); } @Benchmark public Object top_10_frames() throws Exception { return doCall(() -> { StackTraceElement[] stackTrace = new Exception().getStackTrace(); return Arrays.copyOfRange(stackTrace, 0, 10); }, stackDepth); } }

Slide 36

Slide 36 text

public class StackWalkerBenchmark { public static final StackWalker STACK_WALKER = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); @Benchmark public Class> caller_class() throws Exception { return doCall(() -> STACK_WALKER.getCallerClass(), stackDepth); } @Benchmark public int stack_depth() throws Exception { return doCall( () -> STACK_WALKER.walk(stream -> stream.count()).intValue(), stackDepth); } @Benchmark public Object top_10_frames() throws Exception { return doCall( () -> STACK_WALKER.walk(s -> s.limit(10).collect(toList())), stackDepth); } }

Slide 37

Slide 37 text

StackWalker Exception 1 31 8 30 5 30 Stack depth 10 caller_class stack_depth top_10_frames Time, µs/op

Slide 38

Slide 38 text

StackWalker Exception 1 105 29 105 5 105 Stack depth 100 caller_class stack_depth top_10_frames Time, µs/op

Slide 39

Slide 39 text

New APIs

Slide 40

Slide 40 text

jdk-api-diff https://github.com/AdoptOpenJDK/jdk-api-diff

Slide 41

Slide 41 text

public class ArraysBenchmark { @Benchmark public int builtin() { return Arrays.mismatch(a, b); } @Benchmark public int handRolled() { byte[] a = this.a; byte[] b = this.b; int length = Math.min(a.length, b.length); for (int i = 0; i < length; i++) { if (a[i] != b[i]) { return i; } } return a.length == b.length ? -1 : length; } }

Slide 42

Slide 42 text

hand_rolled builtin 296 25 33 9 7 6 ArraysBenchmark prefixSize=1000 prefixSize=100 prefixSize=10 Time, ns/op

Slide 43

Slide 43 text

Recap ● 6 month release cycle ● jlink and AOT help with footprint and startup performance ● Compact Strings and optimized String concat make a huge difference ● New APIs that improve performance of different tasks (StackWalker, Arrays.mismatch(), etc.)

Slide 44

Slide 44 text

References 1) https://github.com/vyazelenko/performance-after-eight 2) http://psy-lob-saw.blogspot.ch/2018/01/what-difference-jvm-makes.html 3) https://www.opsian.com/blog/java-10-with-g1/ 4) http://openjdk.java.net/projects/code-tools/jmh/ 5) http://openjdk.java.net/projects/code-tools/jol/ 6) https://github.com/AdoptOpenJDK/jdk-api-diff

Slide 45

Slide 45 text

Questions? @DVyazelenko