Two weeks ago was the first Riviera JUG (fr) event for the 2024/2025 season: Valhalla va-t-il corriger l’erreur à 1 milliards de dollars? I’m not coding with Java on a daily basis, but seeing a presentation by José Paumard et Rémi Forax seemed a good opportunity to learn about nitty gritty implementation details of the JVM and how it deals with computer architecture constraints.

The plot is about project Valhalla which deals with augmenting Java object model with value objects (in different steps delivered along multiple Java versions). There was a good explanation about the difference between each mode, trades off between what is gained and given up on both sides. The general idea is that by removing identity (being able to point to the same instance as a reference from multiple places), many optimizations can be unlocked that bring processing speed of value classes close to primitives. But it also comes with limitations on what one can do with value objects.

My main take is that Valhalla will progressively improve the readability of source code and reduce silly errors. Currently when a number has to be passed, it is typed as a double or int. This can lead to functions with multiple parameters having similar types, very similar to stringly-typed code.

class MyThing {
  public int doStuff(double speed, double duration) {
    // ...
  }
}

The called can be confuse and invert parameter order: .doStuff(10.0, 16.0). Of course modern IDEs will give tooltips with each parameter name but it’s a shame this is even possible to mix parameters. This is also unclear about units, which often mean adding the unit as a suffix to parameter name: doStuff(double speedKph, double durationMs). The clean solution to those risks is actually creating dedicated wrappers which enforce type: you are not manipulating a number, your are manipulating a speed! Wrappers can enforce constraints, like speed being positive. Wrappers can restrict operations to be performed on a type: it doesn’t make sense summing a speed and a duration, but multiplying a speed with a duration gives a distance. Wrappers can also provide factory methods for safely converting unsafe input at application boundaries.

public final class Speed {
  private static final double speed;

  private Speed(double speed) {
    this.speed = speed;
  }

  public double getKph() {
    return speed;
  }

  public Distance multiply(Duration duration) {
    return Distance.fromKm(speed * duration.getHours());
  }

  public static Speed fromKph(double kph) {
    if (speed < 0) {
      throw new IllegalArgumentException("Speed must be positive: " + kph);
    }
    return new Speed(kph);
  }
}

class MyThing {
  public int doStuff(Speed speed, Duration duration) {
    // ...
  }
}

Before Valhalla adding these wrappers incurs a performance penalty because of object references, increasing memory consumption, and requires performing additional jumps in memory. And thus few are adding these safer wrappers. The ultimate idea of Valhalla is to remove the penalty by having those wrappers behave like the primitive they embed (they can embed multiple primitives, like a complex number holding two doubles!): their values are packed directly in the object or array without references.

As I expected seeing how these optimizations are performed and understanding the limitations that CPU architectures creates was fascinating, but the real impact on my daily life is the new expressivity it unlocks.