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/15) and valhalla (Java ??)
16 GB of RAM – Pointer dereference ~~ 500 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 decides if it is inlined or not • volatile/static fields are never 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; }
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 val = box.shared; if (val.x != val.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 val = box.shared; if (val.x != val.y) { throw new AssertionError("oops"); } } Two stores Two reads Unprotected concurrent access
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
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); } }
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
1, 2 => We are here now ! – Lword 10 - preview release in Java • Support erased generics – Lworld 50 • Retrofit primitives to be inline • Support Value Based Classes – Lworld 100 – finale release • Reified generics
perf regression in existing code ! Doesn’t work well with real applications Requires too much boxing • println(Object) • generics: collections, streams
of indirect type (array of pointer) • 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 – 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 – I prefer the former one (demote ==)
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
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 (eclair)
1, 2 => We are here now ! – Lword 10 - preview release in Java • Support erased generics – Lworld 50 • Retrofit primitive to be inline • Support Value based classes – Lworld 100 – finale release • Reified generics