Leyden has been relying pretty heavily on training runs which I get, but man is that a pretty hard burden to setup.
The tooling for doing these sorts of training runs doesn't really exist, you end up needing to do something more bespoke as part of your build pipeline if you want it there. That can be especially tricky with more complicated applications like the ones I maintain.
I like what these can deliver, but dislike the effort needed to get it going.
I completely agree. I know that there has been some work on command line ergonomics for this stuff, but my hope is that after the bulk of the Leyden work is done, another project starts up aimed at unifying all of the various disparate tools that modern Java now has.
You have training runs to generate bytecode/compiled code, you have jlink to create a distributable JRE for your app, leaning on jdeps and various other tools to correctly create it, you have the venerable jar file, but all in all the process of actually getting a java project ready to distribute in a way that doesn't presuppose a relevant pre-installed JRE on the user's side, and which takes advantage of the various facilities available to modern Java is still a pretty bespoke and painful process.
Compared to Go and Rust, and just throwing a static binary to someone or some server, Java kind of sucks. And that's unfortunate because the language, the runtime, and even the dependency situation all feels pretty competent, there's not some huge gulf that makes Java "old". Its really just the every about setting up a project and then distributing it, so the "start" and "end" of a project, that currently sucks, and I fear that so much of the great work the Java team is doing is going to go largely unused if they don't work to make it easier for the average developer to utilize it.
The complexity of training depends heavily on your goals. If your goal is to improve startup time, it's actually pretty simple.
If your goal is peak performance across the board and you can't wait for runtime optimizations to kick in, you're going to have a harder time. But in many of the things I've worked on, startup time has been a bigger issue than JIT->peak.
Don't forget GCJ. It wasn't removed from GCC until about 2016, a nearly 20-year long run, though interest had waned many years prior.
I don't remember if GCJ supported loading and running code dynamically with a built-in JIT or interpreter. I think it was just pure AOT, which caused some compatibility headaches.
They were not the only ones, see PTC, Aicas, IBM, and a few others.
The big difference until GraalVM and OpenJ9 became available as open source, was that all those vendors made AOT compilation a commercial feature, and thus most devs never cared they existed.
In fact GraalVM was one of the main reasons Excelsior JET eventually closed doors.
Why would you not AOT compile at install time?! That would allow the compile to use CPU-specific features? And put a compiled preamble for class initialization on applicable functions that patches the VTABLE to skip the class initialization code.
There is no install time in Java. If you want something to happen at install time you'd have to ship a shell script with the installer that does a brief training run when the application is first started.
That would place significant limitations on the applications that can be compiled. As the JEP explains: “Features such as dynamic class loading, dynamic linkage, dynamic dispatch, and dynamic reflection bring vast expressive power, and have been fundamental to the platform's success. HotSpot handles these features naturally, while static compilers struggle with them. Even heroic amounts of static analysis cannot make up for the fact that these features require many decisions to be made at run time. Implementors of static compilers for Java code have therefore resorted to incompatible constraints, such as closed-world assumptions, and to putting significant burdens on developers, such as having to identify in advance the classes eligible for reflection.”
Therefore I don’t see AOT-only becoming an integral part of standard Java in the foreseeable future.
If you haven't checked it out yet, GraalVM's Native Image [1] already let's do AOT compilation suitable for distribution. You have to do some configuration to deal with reflection and dynamic class loading since there's no JVM in the produced build, but it comes with tooling to simplify that. And that restriction is being addressed by Project Crema [2].
That won't be necessary when 544 lands. The only thing you'd gain is not having to ship the JIT compilers in a jlink build. Also, I'm fairly certain that the JIT compilers can be turned off with some combination of runtime flags to ensure only the AOT generated code is executed.
Leyden has been relying pretty heavily on training runs which I get, but man is that a pretty hard burden to setup.
The tooling for doing these sorts of training runs doesn't really exist, you end up needing to do something more bespoke as part of your build pipeline if you want it there. That can be especially tricky with more complicated applications like the ones I maintain.
I like what these can deliver, but dislike the effort needed to get it going.
I completely agree. I know that there has been some work on command line ergonomics for this stuff, but my hope is that after the bulk of the Leyden work is done, another project starts up aimed at unifying all of the various disparate tools that modern Java now has. You have training runs to generate bytecode/compiled code, you have jlink to create a distributable JRE for your app, leaning on jdeps and various other tools to correctly create it, you have the venerable jar file, but all in all the process of actually getting a java project ready to distribute in a way that doesn't presuppose a relevant pre-installed JRE on the user's side, and which takes advantage of the various facilities available to modern Java is still a pretty bespoke and painful process.
Compared to Go and Rust, and just throwing a static binary to someone or some server, Java kind of sucks. And that's unfortunate because the language, the runtime, and even the dependency situation all feels pretty competent, there's not some huge gulf that makes Java "old". Its really just the every about setting up a project and then distributing it, so the "start" and "end" of a project, that currently sucks, and I fear that so much of the great work the Java team is doing is going to go largely unused if they don't work to make it easier for the average developer to utilize it.
The complexity of training depends heavily on your goals. If your goal is to improve startup time, it's actually pretty simple.
If your goal is peak performance across the board and you can't wait for runtime optimizations to kick in, you're going to have a harder time. But in many of the things I've worked on, startup time has been a bigger issue than JIT->peak.
Yes, OpenJ9 and ART do it much more easily, the JIT cache is updatable across executions, so there isn't an explicit training run required.
Excelsior JET did that 25 years ago: https://en.wikipedia.org/wiki/Excelsior_JET
It's a shame that Sun/Oracle never partnered with them to bring native apps. This could have saved Java on the desktop.
Don't forget GCJ. It wasn't removed from GCC until about 2016, a nearly 20-year long run, though interest had waned many years prior.
I don't remember if GCJ supported loading and running code dynamically with a built-in JIT or interpreter. I think it was just pure AOT, which caused some compatibility headaches.
They were not the only ones, see PTC, Aicas, IBM, and a few others.
The big difference until GraalVM and OpenJ9 became available as open source, was that all those vendors made AOT compilation a commercial feature, and thus most devs never cared they existed.
In fact GraalVM was one of the main reasons Excelsior JET eventually closed doors.
The JRockit JVM had it already 20 years ago, and the company was even bought by Oracle shortly afterwards.
IBM's J9 had AOT for a long time too. I wonder how JEP 544 compares.
The only thing that could possibly save Java on the desktop would require Oracle not owning it.
Why would you not AOT compile at install time?! That would allow the compile to use CPU-specific features? And put a compiled preamble for class initialization on applicable functions that patches the VTABLE to skip the class initialization code.
There is no install time in Java. If you want something to happen at install time you'd have to ship a shell script with the installer that does a brief training run when the application is first started.
What are the architectural differences between this and Android's ahead-of-time runtime?
This JEP is essentially about caching the JIT's generated code for later runs. The JIT is still free to discard the cache if it deems it worthwhile.
Startup time being the main benefit means simple training runs get you most of the value. Going for peak throughput is where the setup gets heavy.
I hope at some point we'll get AOT-only mode (or compile to native) and maybe even cross-compilation.
That would place significant limitations on the applications that can be compiled. As the JEP explains: “Features such as dynamic class loading, dynamic linkage, dynamic dispatch, and dynamic reflection bring vast expressive power, and have been fundamental to the platform's success. HotSpot handles these features naturally, while static compilers struggle with them. Even heroic amounts of static analysis cannot make up for the fact that these features require many decisions to be made at run time. Implementors of static compilers for Java code have therefore resorted to incompatible constraints, such as closed-world assumptions, and to putting significant burdens on developers, such as having to identify in advance the classes eligible for reflection.”
Therefore I don’t see AOT-only becoming an integral part of standard Java in the foreseeable future.
If you haven't checked it out yet, GraalVM's Native Image [1] already let's do AOT compilation suitable for distribution. You have to do some configuration to deal with reflection and dynamic class loading since there's no JVM in the produced build, but it comes with tooling to simplify that. And that restriction is being addressed by Project Crema [2].
[1] -- https://www.graalvm.org/latest/reference-manual/native-image... [2] -- https://github.com/oracle/graal/issues/11327
progress, not perfection! Java is on fire right now with new stuff landing. We'll get there!
We have that in Codename One, yes it's not really "Java" but it compiles bytecode AOT and cross compiles to some of the platforms.
That won't be necessary when 544 lands. The only thing you'd gain is not having to ship the JIT compilers in a jlink build. Also, I'm fairly certain that the JIT compilers can be turned off with some combination of runtime flags to ensure only the AOT generated code is executed.
GraalVM native-image already provides an AOT-only executable.