byte short int long float double boolean char
==
compares primitives by value, not identitynull
⚠️false
) ⚠️Codes like a class, acts like an int
Function<Integer, Boolean>
vs. Predicate<Integer>
vs. IntFunction<Boolean>
vs. IntPredicate
==
, hashCode
derived from fields, not addresspublic value record Point(double x, double y) { public Point moveBy(double dx, double dy) { return new Point(x + dx, y + dy); } }
Object
is an identity class, but value classes extend itinterface JSONValue { ... } abstract value class JSONPrimitive extends JSONValue { ... } value record JSONNumber(double value) extends JSONPrimitive { ... } enum JSONBoolean extends JSONPrimitive { ... } // identity class
git clone https://github.com/openjdk/valhalla/ cd valhalla bash configure
make images
valhalla/build/linux-x86_64-server-release/images/jdk
sdk install java valhalla /path/to/valhalla/build/linux-x86_64-server-release/images/jdk
value
class syntax is supported in current Valhalla buildjavac --enable-preview --source 25 ... java --enable-preview ... jshell --enable-preview
Point p = new Point(0, 0); for (int i = 0; i < ITERATIONS; i++) p = p.moveBy(Math.random() - 0.5, Math.random() - 0.5); System.out.println(p);
time java --enable-preview withvalhalla.Brownian Point[x=619.9117803091456, y=2227.141928668786] real 0m7.806s
0m8.178s
doubles
: 16 bytesPoint
heap objectjava --enable-preview -XX:StartFlightRecording=filename=novalhalla.jfr novalhalla.Brownian
novalhalla.jfr
into Java Mission ControlPoint
objects (close to 3GiB), busy GChsdis-amd64.so
from https://www.chriswhocodes.com/hsdis/ into lib
directoryexport LD_LIBRARY_PATH=lib
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly \ -XX:+LogCompilation -XX:LogFile=logfilename
java --enable-preview -jar jitwatch-ui-1.4.9-shaded-linux-x64.jar
javac
generates bytecodesjava
interprets bytecodes...Point.moveBy
Point.moveBy
Math.random
is much more expensive 😞 mvn archetype:generate \ -DinteractiveMode=false \ -DarchetypeGroupId=org.openjdk.jmh \ -DarchetypeArtifactId=jmh-java-benchmark-archetype \ -DgroupId=com.horstmann \ -DartifactId=valhalla-benchmark \ -Dversion=1.0
javac.target
to 25 and add this inside configuration
of maven-compiler-plugin
:
<compilerArgs> <arg>-verbose</arg> <arg>--enable-preview</arg> <arg>-proc:full</arg> <arg>--add-exports</arg> <arg>java.base/jdk.internal.vm.annotation=ALL-UNNAMED</arg> </compilerArgs>
mvn clean install java --enable-preview -jar target/benchmarks.jar Benchmark1
transform
tweaks and swaps x and y:
/* value */ record Point(double x, double y) { ... public Point transform() { return new Point(y + 1, 0.5 - x); } }
p = p.transform()
10_000_000 timesBenchmark Mode Cnt Score Error Units c.h.novalhalla.Benchmark1.main avgt 25 69213.839 ± 1526.112 us/op c.h.withvalhalla.Benchmark1.main avgt 25 0.006 ± 0.001 us/op
transform
unrolls loopp.transform().transform().transform().transform()
equals p
!transform
Benchmark Mode Cnt Score Error Units c.h.novalhalla.Benchmark2.main avgt 25 47725.264 ± 6597.810 us/op c.h.withvalhalla.Benchmark2.main avgt 25 24847.389 ± 3120.296 us/op
null
: Point target = null;
null
:
Point! origin = new Point(0, 0); // Syntax may change
null
assignment:
origin = null; // Compile-time error
Point![] path = ...; Object[] objs = path; objs[0] = null; // NullPointerException
LocalDate
null
super(...)
, or with initializer expression, or assigned in object initializerString![] labels; labels = new String![]{ "x", "y", "z" }; labels = new String![100]{ "" }; // strawman syntax labels = new String![100]{ i -> "x"+i }; // strawman syntax
super
possible for arbitrary classespublic value class Node { private Object data; private Node next; public Node(Object data, Node next) { this.data = data; this.next = next; } ... }
private Node! next;
value
modifier)!
will come in phase 2jdk.internal.vm.annotation
package for phase 2 features@NullRestricted
on a field (instead of !
)@Strict
on a field that is strictly initialized@LooselyConsistentValue
to allow tearing (instead of marker interface, see below)jdk.internal.value.ValueClass
lets you construct null-restricted arraysjavac --add-exports java.base/jdk.internal.vm.annotation=ALL-UNNAMED \ --add-exports java.base/jdk.internal.value=ALL-UNNAMED ... java --add-exports java.base/jdk.internal.value=ALL-UNNAMED ...
Point[] path = (Point[]) ValueClass.newNullRestrictedNonAtomicArray(Point.class, NPOINTS, new Point(0, 0)); for (int i = 1; i < path.length; i++) { path[i] = path[i - 1].transform(); } System.out.println(path[path.length - 1]); Thread.sleep(60_000);
jcmd withvalhalla.Points GC.class_histogram | head
num #instances #bytes class name (module) ------------------------------------------------------- 1: 1 1600000016 [Lwithvalhalla.Point;
double
🎉long
, double
may tearvalue record Point(double x, double y) implements LooselyConsistentValue {}
volatile
, locksjava --add-exports java.base/jdk.internal.value=ALL-UNNAMED --enable-preview withtearing.Main Point[x=7.0, y=6.0] Point[x=13.0, y=14.0] ... real 0m0.367s time java --add-exports java.base/jdk.internal.value=ALL-UNNAMED --enable-preview notearing.Main real 0m27.675s
value
modifier!
syntaxInteger
, Double
, etc.Optional
, OptionalInt
, etc.java.time
classes LocalDate
, Instant
, etc.ArrayList<int>
use int
or Object
bytecodes?int[]
or Integer![]
is different than for Object[]
List<int>
a subtype of the raw List
or wildcard List<?>
type?Optional<T!>
)get
method of Map<String, int>
can't return null
static <T extends Comparable<? super T>> void sort(T[] a) static void sort(int[] a)
inline
classesL
and values Q
—no morewithfield
and defaultvalue
—no moreList<int>
is backed by an int[]