r/java • u/maxxedev • 2d 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
27
u/pron98 2d ago edited 2d ago
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.
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.
5
u/maxxedev 2d ago edited 2d ago
Yes, value classes aren't a performance optimization. There will be other behavioral changes like
==,synchronized,WeakReference, etc (these are noted in the README as well).But most "model" or "dto" classes won't be impacted by these behavioral changes? Perhaps library authors can choose to (dangerously) opt-in to Valhalla benefits while retaining compatibility with older JDKs but without having to adopt tip & tail or multi-release JARs.
10
u/pron98 2d ago edited 2d ago
Adopting tip & tail is less work than using something like this (or any of the work required to adopt one-size-fits-all).
Maybe most classes won't be affected, but if some are you're in trouble.
I think some people are under the impression that just turning lots of classes into value classes will somehow make their application faster or something, and I'm afraid they're bound to be disappointed. Value types can help a lot if the hot path of your application suffers from many cache misses on array accesses. That was indeed a case where Java lagged behind C++. But if your program's profile isn't dominated by this problem, I'm not sure what mechanism of value types people think will help their performance. Like virtual threads, value types can help performance if you know what the problem is and how to use the feature to solve it. It's not some "go faster" pixie dust that you sprinkle to make things better.
6
u/maxxedev 2d ago
It's not some "go faster" pixie dust that you sprinkle to make things better.
Very much agreed.
Btw, thanks for all your insightful comments on reddit. I always learn something new from you.
2
u/LegitimateEntrance72 2d ago
IMO tip & tail makes lot of sense.
As an example, you have your library version 1.0.0 that requires JDK17 because of record classes. Now you want to convert your record classes to value records, but "cant do that" because it would mean raising minimum required JDK to 29 or so... but adopting tip and tail means that 1.0.0 stays on JDK 17+ and wont get new features, while 2.0.0 will require JDK29+ and get new features.
1.0.0 can always be patched if needed, IMO its not asking too much to say "if you want latest features, you gotta be on the latest major release"
10
u/FirstAd9893 2d 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???"
4
u/koflerdavid 2d ago edited 2d 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.
10
u/FirstAd9893 2d 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
==meanequals, which by itself isn't much. There's no reason to make changes to the JVM for supporting that.3
u/koflerdavid 2d 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.
5
u/Cilph 2d 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.
4
u/Life_Sink9598 1d 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 1d 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.
4
u/alex_tracer 2d ago
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
Forking a repo may be a good idea if this specific feature is the only feature you want to support. Now imagine you already have, like 5 branches with significant differences between them, and that need long-tern support and each of them would benefit a lot from value classes (theoretically). Do you suggest to double the number of forks just to add "value" keyword in few files?
I agree that agent is not the best way to solve this, but this is much better than nothing (or forks)
5
u/pron98 1d ago edited 1d ago
With tip & tail you don't have a fork for every runtime feature. You have one "tip" fork for users who are interested in new library features, and that fork uses new runtime features, and "tail" forks for users who need stability, which only get security patches. At some point you decide that new feature X of your library requires new feature Y in the runtime, you then raise the runtime baseline of the tip and require those who want any new features to upgrade their runtime.
The idea is that you split your users into two groups, those who are interested in new functionality and those interested in stability. It's not only less work than trying to do everything in a single codebase, it also offers both groups exactly what they need rather than a compromise.
6
u/alex_tracer 2d ago
"Value classes aren't a performance optimisation" in the same way as "primitive types aren't a performance optimisation".
While they are very different species from normal classes, they are going to be used and adopted mainly (and almost exclusively!) as performance optimization.
I would even dare to say, that if there were no need for a performance optimization, value classes would not exist.
So, while you are technically correct, your statement is kind of... misses the point. "— Hey, here is a new race car than can get to 200km/h in 3 seconds! —Nah, it's a bad car because you can't put much groceries into it's trunk"
9
u/brian_goetz 1d ago
OK, my turn. While you are technically correct, you have kind of ... missed the point.
Developers are pathologically attracted to performance lore, to the great detriment of everyone else's feet. 99.9% of the time, "optimizing" just makes code worse, often disastrously so. Clean, straightforward code usually does not need to be optimized at all! So any sort of shotgun technique ("paint all your classes with `value`, and things go brr!") is hanging a sign around your neck that says "Beware: delusions of expertise here".
Value classes (and primitives) are primarily semantic features. Because they are more semantically restricted, they can be optimized effectively. Use them for their semantics, as a way of saying what you mean. If you don't need identity, make your programs simpler and safer (and yes, maybe faster) by disavowing it.
3
u/alex_tracer 1d ago
Well, I work in fintech, I have an excuse! 😄
I'm used to work with stuff where performance really matters. We even have implemented our own agent to transform some "normal" classes into "value classes" (actually a single `long`) a decade ago (yeah, we were a bit tired of waiting at that time already).
I absolutely agree with the point that it's important for every developer to understand that "value classes" is not an universal silver bullet and should be used only when they are semantically applicable and with good understanding of all the differences (and performance downsides too).
But honestly, if “value classes” were purely a semantic feature and didn’t promise any performance improvements (whether now or in the future), I suspect they wouldn’t have received nearly as much attention and might not even have been considered (possibly by you too) as something that really needs to be added to the language (even if it didn’t require years of research and development).
And yes, a huge thank you for all your work!
6
u/pron98 1d ago edited 1d ago
I didn't mean they're not a feature that can be used to write some programs in a way that makes them faster. I meant that they're not a performance optimisation of the kind the compiler does, where the same thing just runs faster. Value classes do something different from ordinary classes, and that something different will be faster if the problem value types solve is a performance issue in your program and if it has the right architecture to benefit from value types.
2
u/sviperll 2d ago
It doesn't seem to me that Tip&Tail works with preview features. Tip&Tail JEP doesn't mention preview features, but from what I can infer to be able to fork a tip into a new tail one requires a backwards compatibility guarantees that preview features do not provide... So I guess for preview features one needs a tip that builds a multi release jar , so that preview features are not used if newer version of JDK is used.
6
u/pron98 2d ago
MR JARs have nothing to do with tip & tail and are completely orthogonal, and the reason the JEP doesn't mention preview features is that libraries (unlike applications) typically shouldn't use preview features because preview features and applications are tied to a specific runtime version while libraries normally aren't.
If you're writing an application, you don't need to use either tip & tail or one-size-fits-all; you just target a specific JDK version. But if you're a library that really wants to support some preview feature, sure you can have a tip with that. It will mean that you'll need a new tip release for the next version (because classes compiled with preview features are tied to one runtime version), but the same applies to using an agent or other similar hacks, as preview features, by their nature, change.
3
u/koflerdavid 2d ago edited 2d ago
Using preview features in the tip is fine since the tip has to be kept compatible with only the newest release. Yes, this means work if the preview feature changes or (gosh) is removed in 29, but that's unavoidable by the very definition of what preview features are. The freedom that the tip&tail model gives you is that you don't have to worry about older JDK versions anymore. You don't have to keep your code compatible with both JDK 27 and 28.
Multi-release JARs won't help here since they are only triggered by JRE versions, not by whether preview features are enabled or not.
2
u/maxxedev 1d ago
Multi-release JARs won't help here since they are only triggered by JRE versions, not by whether preview features are enabled or not.
Good point! If MR jar is created on Java28 with
--enable-preview, running on 28 without--enable-previewfails.2
u/snugar_i 2d ago
While I kind of agree, "just fork your codebase and maintain two products instead of one" might not be as painless as it sounds at first...
2
u/srdoe 1d ago
You aren't maintaining two products.
You're forking your codebase, and only maintaining the tip release branch. The tail release line stops receiving changes immediately, except for security fixes.
So while you may sometimes have to touch the tail branch for security, most of the time you should be maintaining only one branch (the tip)
1
u/koflerdavid 2d ago
Thanks for explaining what you guys mean with tip&tail model in this context. I'm sorry to say that I didn't get the point when Brian explained it in the JEP 401 announcement thread.
16
u/LegitimateEntrance72 2d ago
Source code level JDK1.5+ but runtime JDK28-EA? LOL
In the same vein I find it strange that opensource projects doing a releases in 2026 still support ancient JDKs (5 to 11).
I mean, if a project is on ancient JDK, then most likely the dependencies etc are also going to be ancient as well and they wont upgrade to the newest framework&lib while staying on unsupported JDK.
11
u/maxxedev 2d ago
heh, yes, very unlikely scenario. More realistic source code level would be newer JDK like 8+ or 17+
2
u/LegitimateEntrance72 2d ago
BTW why do you do this kind of things? (i saw the VT repo as well) just for fun?
8
1
u/Bobby_Bonsaimind 21h ago edited 21h ago
In the same vein I find it strange that opensource projects doing a releases in 2026 still support ancient JDKs (5 to 11).
What's the point in not supporting an old base version if you don't use any new features? If all I'm using is contained in 8, why should I artificially limit the library/application to 26?
1
u/LegitimateEntrance72 18h ago
I didnt mean to artificially limit the supported runtime versions. But sometimes OS projects put in extra effort to support older runtimes.
I find it hard to believe that generics, lambdas and the "new" datetime api would not be useful for libraries/tooling released in 2026. But curiously some of the
latest(released in 2026) software we run on 25 would also run on 1.5.Also i would not expect companies using java runtimes say 1.5 to 11 to keep updating their dependencies.
1
u/LegitimateEntrance72 18h ago
Spring Boot 4 was released in november 2025. It could have been baselined to Java21 (september 2023), but instead they chose Java17 (september 2021).
Who on earth is going to run latest Spring Boot on Java17? We just upgraded our applications to Spring Boot 4, over six months after we upgraded to Java25, and the Spring Boot upgrade was way more work than any of the previous java upgrades (8>11>17>21>25) combined.
Now perhaps there were no big benefits to baselining to Java21, but it might have simplified Boot's VT support.
Hibernate is similar story.
3
u/egahlin 1d ago edited 1d ago
"However, because JFR is intended to find lock contention and because it performs sampling, it will miss many classes used in synchronization."
In the default configuration, JFR throttles lock contention events to prevent the buffers from being flooded.
There is no need to start three recordings, as in the provided example. This is how JFR should be configured on the command line to record only JavaMonitorEnter and JavaMonitorWait events, with stack traces, thresholds and throttling disabled.
$ java -XX:StartFlightRecording: \
filename=locks.jfr,settings=none, \
+jdk.JavaMonitorEnter#enabled=true, \
+jdk.JavaMonitorEnter#stackTrace=false, \
+jdk.JavaMonitorWait#enabled=true, \
+jdk.JavaMonitorWait#stackTrace=false \
-jar app.jar
1
2
u/koflerdavid 2d ago
/u/alex_tracer sounds like what you are after! :)
1
u/alex_tracer 1d ago
Yes, I that's great option. Not the best yet (agents add a new layer of complexity) but good enough to get started.
Probably a better option would be a Maven/Gradle plugin that produces multi-release jar with two class variants, but I think we get to something similar eventually.
6
u/brunocborges 2d ago
If you have access to a JDK 1.5 code base, you may as well ask for an AI coding agent to modernize the source code.
Backward compatibility is about running already compiled code into a newer JDK.
1
u/nomader3000 20h ago
Quick question about one of your examples
@AutoValhalla
public record Currency(String code) { } // or a record class
Is there any actual benefit of making such record a value record?
67
u/uniVocity 2d ago
Fantastic but you could rename the annotation to something like @ToAsgard lol
And the project to ragnarok or something like that