Experimenting with Valhalla Early Access Build 26-jep401ea2+1-1 (2025/10/10)

 

A new build of Valhalla has been made available on https://jdk.java.net/valhalla/ this version is built on top Java 26 early build.

It’s been a while since I tried to the previous Valhalla Early access in 2022

A lot has has changed since then.

In the previous version they had concept of value (without identity) and primitive class (without identity and not nullable)

Now they have simplified to value class (without identity)

Whether a value can be nullable is or not, will be delivered separately by this JEP draft: Null-Restricted Value Class Types (Preview)

Reference Code – No Value class


public class Main {
    static void main() {
        long start = System.currentTimeMillis();
        int length = 10_000_000;
        Point3D[] myArray = new Point3D[length];
        for (int i = 0; i < myArray.length; i++) {
            myArray[i]=new Point3D(0f, 0f, 0f);
        }
        System.out.println("Print last value of array : "+myArray[length-1]);
        Runtime.getRuntime().gc();
        printMemoryUsage();
        System.out.println("Array length : "+myArray.length);
        System.out.println("Total time "+(System.currentTimeMillis() - start)+ " ms");
    }
    private static void printMemoryUsage() {
        System.out.println("MB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024*1024));
    }
    static class Point3D {
        private float x ;
        private  float y ;
        private  float z ;
        public Point3D(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        @Override
        public String toString() {
            return "Point3D{" +
                    "x=" + x +
                    ", y=" + y +
                    ", z=" + z +
                    '}';
        }
    }
}

 

If I run this on my machine I get this output

Print last value of array : Point3D{x=0.0, y=0.0, z=0.0}
MB: 269.9514617919922
Array length : 10000000
Total time 182 ms

 

Now if I do the same using Value class (make Point3D value class)

 


public class Main {
    static void main() {
        long start = System.currentTimeMillis();
        int length = 10_000_000;
        Point3D[] myArray = new Point3D[length];
        for (int i = 0; i < myArray.length; i++) {
            myArray[i]=new Point3D(0f, 0f, 0f);
        }
        System.out.println("Print last value of array : "+myArray[length-1]);
        Runtime.getRuntime().gc();
        printMemoryUsage();
        System.out.println("Array length : "+myArray.length);
        System.out.println("Total time "+(System.currentTimeMillis() - start)+ " ms");
    }
    private static void printMemoryUsage() {
        System.out.println("MB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024*1024));
    }
    static value class Point3D {
        private float x ;
        private  float y ;
        private  float z ;
        public Point3D(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        @Override
        public String toString() {
            return "Point3D{" +
                    "x=" + x +
                    ", y=" + y +
                    ", z=" + z +
                    '}';
        }
    }
}

Print last value of array : Point3D{x=0.0, y=0.0, z=0.0}
MB: 269.80834197998047
Array length : 10000000
Total time 224 ms

Oh dear 🙁 actually ran slower using value class !

 

Currently (in this build for now) value class is restricted to 64bit for flattening.

The point3d class has 3 floats = 32 * 3 = 96 bits

In additional there is also an extra bit also added to keep track of whether the value is null or not.

So it’s actually (32 * 3) +1 = 97 bits

 

Why 64bit restriction for flattening, apparently that is to align with size of registers which are 64 bits, otherwise if you need multiple registers, you are in danger of tearing – where threads might be able to read an inconsistent value.

 

However, in the EA build there is an option to create an array of value classes where you can accept tearning might occur and also specify the values cannot be null

 

Replace these lines


for (int i = 0; i < myArray.length; i++) {
    myArray[i]=new Point3D(0f, 0f, 0);
}

 

with this instead (but note this using a jdk internal class ) so you will need to use this additional command line flags

–add-exports java.base/jdk.internal.value=ALL-UNNAMED –enable-preview


Point3D[] myArray = (Point3D[]) ValueClass.newNullRestrictedNonAtomicArray(Point3D.class, 10_000_000, new Point3D(0f, 0f, 0f));

 

Full Code (Enabling Null-Restriction and Non-Atomicity )


import jdk.internal.value.ValueClass;

public class Main {
    static void main() {
        long start = System.currentTimeMillis();
        int length = 10_000_000;
        Point3D[] myArray = (Point3D[]) ValueClass.newNullRestrictedNonAtomicArray(Point3D.class, 10_000_000, new Point3D(0f, 0f, 0f));
        System.out.println("Print last value of array : "+myArray[length-1]);
        Runtime.getRuntime().gc();
        printMemoryUsage();
        System.out.println("Array length : "+myArray.length);
        System.out.println("Total time "+(System.currentTimeMillis() - start)+ " ms");
    }
    private static void printMemoryUsage() {
        System.out.println("MB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024*1024));
    }
    static value class Point3D {
        private float x ;
        private  float y ;
        private  float z ;
        public Point3D(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        @Override
        public String toString() {
            return "Point3D{" +
                    "x=" + x +
                    ", y=" + y +
                    ", z=" + z +
                    '}';
        }
    }
}

With above the output is now

Print last value of array : Point3D{x=0.0, y=0.0, z=0.0}
MB: 40.89228820800781
Array length : 10000000
Total time 109 ms

🙂

Wow!! From 270 mb we went down to 41 mb ! a huge reduction in memory usage!

Also the execution time was reduced from 182ms to 109ms

 

Hopefully this will be promoted from preview to something that ships in Java soon. Can’t wait!

Leave a Reply

Your email address will not be published. Required fields are marked *