Open Source dev OpenJDK, ASM, Tatoo, Pro, etc Java Spec expert One of the Father of invokedynamic (Java 7) lambda (Java 8), module (Java 9), constant dynamic (Java 11) record (Java 14) and valhalla (Java ??)
GB of RAM – Pointer dereference ~~ 300 additions Java Model – Every objects are still hold by references • Unless the JIT can prove that the identity is not used Escape Analysis (if … )
big ? – Concurrent read/write of an inline object Solution: Inline objects are inlineable – VM choose if it is inlined or not • volatile/static fields are not inlined
public inline class Complex { private final double re; private final double im; public Complex(double re, double im) { this.re = re; this.im = im; } public Complex add(Complex c) { return new Complex(re + c.re, im + c.im); } }
class IntBox { private final int value; public IntBox(int value) { this.value = value; } public int intValue() { return value; } public static IntBox zero() { return new IntBox(0); } }
private static final IntBox[] ARRAY = new IntBox[100_000]; static { range(0, ARRAY.length).forEach(i -> ARRAY[i] = new IntBox(i)); } @Benchmark public int sum_IntBox() { var sum = 0; for(var value : ARRAY) { sum += value.intValue(); } return sum; }
? private static final IntBox[] ARRAY = new IntBox[100_000]; static { range(0, ARRAY.length).forEach(i -> ARRAY[i] = new IntBox(i)); Collections.shuffle(Arrays.asList(ARRAY)); } @Benchmark public int sum_IntBox() { var sum = 0; for(var value : ARRAY) { sum += value.intValue(); } return sum; }
1, 2 => We are here now ! – Lword 10 - preview release in jdk • support erased generics – Lworld 100 – finale release • primitive as inline type + specialized generics
as late as possible class Holder { … } class AnotherClass { public static Holder getMeAHolder() { return null; } } Even when getMeAHolder() is executed, Holder is not loaded
early – memory layout – method calling sequence • Scalarization even if method not inlined ! – verifier checks null pollution solution: prefix class by ‘Q’ instead of ‘L’ ‘Q’ => loaded early
fields being initialized public inline class IntBox { private final int value; public IntBox(int value) { // here I can see the mutation !! this.value = value; } ... }
fields being initialized => not truly immutable Solution: only initialize through a static factory – Compiler transforms constructors to static methods “this” should not escape ! – Add 2 new opcodes: defaultvalue and withfield
y; public Value(long x, long y) { … } } public static void main(String[] args) { var box = new Object() { Value shared; }; var zero = new Value(0, 0); var one = new Value(1, 1); new Thread(() -> { for(;;) { box.shared = zero; } }).start(); new Thread(() -> { for(;;) { box.shared = one; } }).start(); for(;;) { var value = box.shared; if (value.x != value.y) { throw new AssertionError("oops"); } }
y; public Value(long x, long y) { … } } public static void main(String[] args) { var box = new Object() { Value shared; }; var zero = new Value(0, 0); var one = new Value(1, 1); new Thread(() -> { for(;;) { box.shared = zero; } }).start(); new Thread(() -> { for(;;) { box.shared = one; } }).start(); for(;;) { var value = box.shared; if (value.x != value.y) { throw new AssertionError("oops"); } } Two stores Two reads Unprotected concurrent access
of indirect type (array of pointers) • an array of inline type (array of structs) => size of an array cell not constant ! Iterating over an array can be megamorphic ! Solution: Aggressive loop hoisting + loop duplication
available on an inline class but ... – No header • synchronized, wait/notify ? • System.identityHashCode ? – No identity • Meaning of o == o2 if inline objects ?
return false (no pointer) – do a classcheck + components checks • perf regression in existing code ? – may stackoverflow / very slow if recursive – redirect to call equals() • Perf regression ! Still in flux !
but inline classes are not nullable Map<String,Complex> map = ... map.get() – API contract: Can return null – Inline class not nullable: Can not return null
1, 2 => We are here now ! – Lword 10 - preview release in Java • support erased generics – Lworld 100 – finale release • primitive as inline type + specialized generics
nullable, tearable – runtime model • scalarization into registers on stack • inlined into another object/array cells on heap – Object as root of everything • don’t use == – erased generic => lightweight boxing (interface)
List<Complex> l = … // doesn’t compile But using the eclair box is ok List<Complex.box> l = List.of(new Complex(…)); // subtyping Complex c = l.get(0); // unboxing, may throw NPE
So invokevirtual can call an interface Change the VBC to be interface (eclair) – add Optional.val as inline class – JIT: Aggressive non null propagation • At worth, additional nullcheck
y; public Value(int x, int y) { … } } public static void main(String[] args) { var box = new Object() { Value shared; }; var zero = new Value(0, 0); var one = new Value(1, 1); new Thread(() -> { for(;;) { box.shared = zero; } }).start(); new Thread(() -> { for(;;) { box.shared = one; } }).start(); for(;;) { var value = box.shared; if (value.x != value.y) { throw new AssertionError("oops"); } }
y; public Value(int x, int y) { … } } public static void main(String[] args) { var box = new Object() { Value shared; }; var zero = new Value(0, 0); var one = new Value(1, 1); new Thread(() -> { for(;;) { box.shared = zero; } }).start(); new Thread(() -> { for(;;) { box.shared = one; } }).start(); for(;;) { var value = box.shared; if (value.x != value.y) { throw new AssertionError("oops"); } } Two stores Two reads Unprotected concurrent access