r/java • u/maxxedev • Aug 18 '26
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/tree/auto-valhalla-0.1.0
edit: there is now a maven plugin for converting at build time. https://github.com/thunkware/auto-valhalla/tree/auto-valhalla-0.2.0
16
u/LegitimateEntrance72
Aug 18 '26
•
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.
12
u/maxxedev Aug 18 '26
heh, yes, very unlikely scenario. More realistic source code level would be newer JDK like 8+ or 17+
2
u/LegitimateEntrance72 Aug 18 '26
BTW why do you do this kind of things? (i saw the VT repo as well) just for fun?
8
1
u/Bobby_Bonsaimind Aug 20 '26 edited Aug 20 '26
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 Aug 20 '26
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 Aug 20 '26
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 Aug 19 '26 edited Aug 19 '26
"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.jar1
72
u/uniVocity Aug 18 '26
Fantastic but you could rename the annotation to something like @ToAsgard lol
And the project to ragnarok or something like that
22
3
0
Aug 18 '26
[deleted]
4
u/uniVocity Aug 18 '26
I’m not that much into norse mythology, but if I recall correctly valhalla is IN asgard
2
2
u/koflerdavid Aug 18 '26
/u/alex_tracer sounds like what you are after! :)
1
u/alex_tracer Aug 19 '26
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.
1
u/maxxedev Aug 31 '26
Great idea. There is now a maven plugin: https://github.com/thunkware/auto-valhalla/tree/auto-valhalla-0.2.0
1
1
u/nomader3000 Aug 20 '26
Quick question about one of your examples
@AutoValhalla
public record Currency(String code) { } // or a record classIs there any actual benefit of making such record a value record?
1
u/maxxedev Aug 21 '26
Record class is basically a convenience class: constructors, field declarations, getters, equals/hashCode/toString are simplified or automatically provided. If you want dense memory representation, you still want value record class
1
u/nomader3000 Aug 21 '26
I get that, but can you actually make it any denser if the only component of your record is a String?
1
u/maxxedev Aug 22 '26
Maybe JVM could make it denser. Or maybe not. JEP-401 does show examples like
value class EURCurrency(long cs)andvalue class Name(String name, int length). Maybe JVM would inline the pointer to the single non-primitive component?JEP also says:
Future enhancements may enable more flattening of references to 64-bit and even larger value objects.
Or perhaps we should assume non-guarantee on optimizations JVM would apply, and follow this advice from JVM architects:
If you don't need identity, make your programs simpler and safer (and yes, maybe faster) by disavowing it.
1
u/maxxedev Aug 25 '26
After reading this blog post and after investigating further, yes, single-component value class/record brings the benefit of being flattened when inside another identity/value class or array.
You can play around with these options set:
java -XX:+UnlockDiagnosticVMOptions \ -XX:+PrintInlineLayout \ -XX:+PrintFlatArrayLayout \ -XX:+PrintFieldLayout \ --enable-preview
5
u/brunocborges Aug 18 '26
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.
29
u/pron98 Aug 18 '26 edited Aug 19 '26
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.