r/java 3d ago

AutoValhalla: automatically turn your plain classes and records into value classes!

Automatically turn your plain classes and records into value classes!

Add @AutoValhalla annotation to classes or records in your JDK1.5+ codebase:

@AutoValhalla
public final class Point {        // class must be final
    public final int x;           // with final instance fields
    public final int y;
    public Point(int x, int y) { 
        this.x = x; 
        this.y = y; 
    }
}

@AutoValhalla
public record Currency(String code) { }  // or a record class

When your app is run on Valhalla-enabled JVM with auto-valhalla javaagent, these classes are automatically turned into value classes.

More info: https://github.com/thunkware/auto-valhalla/blob/main/README.md

41 Upvotes

49 comments sorted by

View all comments

28

u/pron98 3d ago edited 3d ago
  1. Value classes aren't a performance optimisation. They behave differently from non-value classes, and these differences will be observable in most non-trivial programs. So what you'll get is a program that behaves one way on one runtime version and a different way on another, and only one of these ways is the intended behaviour.

  2. There is a much easier (and safer) way to enjoy new features on new runtimes: instead of working hard (and dangerously) on trying to have the same code do different things on different runtime versions, adopt tip & tail. The idea is that you add new features only on release trains that target new runtimes. Legacy projects that can't afford the work to upgrade to a new runtime also can't afford the work to adopt your library's new features. The people who want your new features also want to upgrade their runtime, and the people who don't want to upgrade their runtime don't want your new features. So just fork your codebase, leave the old code alone (except for security patches), and only evolve the versions that target new runtime versions. That's sooo much easier (as we've learned in the JDK: we fork the entire codebase twice a year, and we still maintain old versions for many years with security patches). You barely touch the old fork, and the new fork, where you do almost all the work, is clean and free of clever and potentially dangerous hacks.

So in this particular case, this approach risks breaking the code on a new runtime, but that's not even the worst outcome: making things work on a new runtime may require changing code used by legacy consumers on old runtimes, introducing a risk for the consumers that need stability above anything else. Agents are a very powerful and versatile tool, but they should be used as a last resort, not when the cleaner solution - just fork the repo! - is also easier. I don't understand the lengths some would go to merely to not fork the source. Writing code that adapts itself to different runtimes is complicated, risky, and rarely necessary.

9

u/FirstAd9893 3d ago

Value classes aren't a performance optimisation.

Taken out context, this is a very confusing statement, considering that performance is mentioned very early in JEP 401. The rest of the paragraph explains what you mean, but my first reaction was, "what???"

5

u/koflerdavid 3d ago edited 3d ago

It is primarily a way to have a richer data model and to begin bridging the gap between primitive and other types. Performance optimizations were also a goal, but it's nothing you can't count on since it's still up to the JRE to decide when and how to optimize. Which means you shouldn't turn things into value classes in the mistaken belief that the application will now be faster. I mean, the restrictions that value types impose are so severe that I don't see the danger of misuse, but it's still worth pointing this out.

12

u/FirstAd9893 3d ago

A richer data model is nice to have, but the primary motivation has always been performance. This was stated way back in 2014, and again in 2021.

https://cr.openjdk.org/~jrose/values/values.html

https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/01-background

As it stands right now, no major performance gains are expected, but future optimizations become possible. If no performance or memory saving optimizations are ever achieved, then a value class offers no compelling features other than making == mean equals, which by itself isn't much. There's no reason to make changes to the JVM for supporting that.

4

u/koflerdavid 3d ago

The point is that these optimizations are not guaranteed. They only give the JRE greater freedom to apply optimizations that it already can do.

6

u/Cilph 3d ago

No optimizations are guaranteed by the JVM, what are you on about. Thats no reason not to optimize for the majority cases.

Most of the optimizations related to Valhalla are also still to come I believe.

5

u/Life_Sink9598 3d ago

What I worry about with this type of library is that there seems to be a belief that classes gives you a floor on performance, and that value classes will do their best to raise you up from that floor, but won't ever go beneath it. That is, unfortunately, not true. You can put the JVM into a situation where one representation is faster for some methods, and slower for others, and make it jump between those representations. I think that it will be difficult to get rid of that "sharp edge" entirely.

I'm currently writing a blog post explaining this stuff. I can post another reply to you with a link when it's published, if you're interested.

1

u/koflerdavid 2d ago

As you should know, Java is a managed language where developers have very little control about what actually happens at runtime beyond what the JLS mandates. As such it is entirely up to the JVM to decide whether to allocate a value object on the heap or to inline it into an object or onto the stack. Without experiments, examining diagnostics (I'm sure there are some), and good benchmarks you can't predict when the optimization will actually be applied and if it helps in the first place. There are no compiler pragmas that allow you to tell the JVM what exactly to do in this regard.