cogman10 1 day ago

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.

  • jgon 1 day ago

    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.

    • tadfisher 1 day ago

      Are you describing jpackage? I'm not sure how you would make it less bespoke, given you are essentially creating a Java runtime for your program. Maybe the Java folks could tree-shake the runtime for you so you wouldn't have to care about their weird module system, but it's not the worst thing to learn.

      • jgon 1 day ago

        jpackage is heavily oriented around installable artifacts, it creates a .deb or .rpm for Linux for example. So if I wanted to throw someone a cli utility I can literally just send them the binary if I’m working in Go, whereas Java they need to use dpkg to install the deb. This somewhat complicates building a docker image as well. It’s the same story for windows and Mac as well. If I’m creating a Swing app or something similar this is very convenient but anything else and jpackage isn’t ideal. Not to mention that I don’t know what the integration is currently with jpackage and the aot cache that Leyden produces but I’m guessing it isn’t super straight forward.

        I’m sure that all of this stuff can be strung together after spending a suitable amount of time trolling through the documentation of the individual tools, but it’s far cry from go build and cargo build, and I think it would be great if Java could narrow the gap in this aspect.

        • samus 14 hours ago

          One could also just use jlink and extract the zip file that it produces.

      • ptx 13 hours ago

        It would be nice if jpackage could bundle the generated AOT configuration data along with a custom action in the MSI package to run the machine-dependent AOT compilation step at install time.

    • mike_hearn 12 hours ago

      What would be in your ideal tool?

      I have a company that makes Conveyor, which is very good at distributing JVM apps. It's primarily meant for desktop apps and replaces jpackage with something that can cross-build, make apps self-updating, auto-minimize the bundled JDK and so on.

      Adapting it for servers is something I think about sometimes, and in fact it does already support that to some extent, but it produces debs for that use case.

      It's just sort of unclear what JVM developers lack here that isn't already solved by build tool plugins and Docker. Or are you thinking about CLI tooling? Because I have something cooking for that which might be pretty nice.

      • jgon 6 hours ago

        My ideal tool would be less tooling, which I know is not a great answer. But what I mean by this is that I can probably cobble together a build pipeline that handles most of this stuff if I spend enough time working with Gradle. And the reality of Java's popularity in the enterprise means that its build tools generally orient around builds that can be crazy and bespoke and so immense flexibility is usually needed. I totally get that need, and I'm not begrudging any of that work.

        I'm speaking more from my view as just an individual Joe Sixpack developer. If I'm not developing an enterprise behemoth, and let's just say I want to whip up a command line utility to do some image processing, or network communication, etc, Java's fantastic standard lib and ecosystem means I can do all of that pretty easily. But when I want to distribute that, what does it look like? What comes out the other side of the CI build? I have to integrate jlink with gradle, which is kind of a pain, I have to somehow integrate a training run and then package up the aot cache output and make sure that the directories are handled properly, and what do I ship to the user? A tar/zip file that they have to somehow copy and decompress to the relevant directory, maybe with #! file in .local/bin to get it on the path? By contrast the situation with Go or Rust is immensely easier, even if that actual development process may almost be harder because I have to spend time faffing about deciding which image library to pull in for Go, or dealing with async for Rust. But once that is actually done, I just ship the binary and simply copy it somewhere to get it on the path, and I never have to worry about reading gradle/maven docs or anything like that.

        So I'll finish by saying that my ideal build tool is probably not a great one. What it is crucially is a simple one. I think that's that gap right now, that Java has these huge, immensely powerful and general build tools, build tools that I am guessing eventually gain equivalents in the Rust and Go worlds when projects grow old and gnarly enough to require a bunch of custom build stuff, but it doesn't have baby's first build tool like go build and cargo, where you can just get an idea and start building and come out the other side with an artifact and 0 time spent on thinking about the build system, until you actually need the kind of complexity that maven and gradle provide. Give me a build tool that assumes the standard src/ and test/ directory layout, then builds everything into some sort of archive file, creates a minimized JRE to run it, accepts an argument for an aot.cache file location, or for the more difficult option runs the program to generate that file and then bundles it up with the archive, bundles all the libraries that I've pulled into the project up into the archive along with all the resources declared for the project as well, and then spits out a single file artifact that I can immediately use and run. Give me a build tool that I don't even realize is there until I actually need that complexity.

        I understand if this is vague and not a great answer, and I also appreciate all the work you've done for the Java community, so thanks for replying.

  • elric 20 hours ago

    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.

  • pjmlp 18 hours ago

    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.

harlan_pdx 19 hours ago

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.

treyd 1 day ago

What are the architectural differences between this and Android's ahead-of-time runtime?

  • Skinney 17 hours ago

    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.

cyberax 1 day ago

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.

  • patwolf 1 day ago

    IBM's J9 had AOT for a long time too. I wonder how JEP 544 compares.

    • samus 13 hours ago

      IMHO the most interesting feature of OpenJ9 is the compile server. One node decides to optimize, sends over the traces, and the compile server sends optimized native code to all nodes. Or a new node joins and could be brought up to speed within a very short time.

  • wahern 1 day ago

    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.

    • java-man 1 day ago

      GCJ was great - I wrote a handset UI entirely in java for a startup in the early 2000's, basically android before android existed, using GCJ. I remember their native interface was somewhat more convenient to use than JNI.

      I wish we had GCJ resurrected, now that the java libraries are GPL'd.

      • alex7o 1 day ago

        Just ask claude to port it to latest gcc :P, pls don't

    • cyberax 1 day ago

      Yeah! I used it around 2003 to integrate a Java PDF parsing library into our C++ app.

      Its biggest downside was the use of Boehm GC that had some issues with large heaps.

    • atgreen 1 day ago

      I worked on this years ago. Gcj did support loading and interpreting bytecode alongside the AOT-compiled code. libffi's closures were originally implemented to support calling interpreted methods from AOT-compiled code, because a class's vtable could include a mix of AOT-compiled and interpreted methods. You could also compile jar files directly into .so files, which would get loaded and used at runtime if a classloader ever loaded the corresponding jar.

  • pjmlp 1 day ago

    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.

  • DonHopkins 1 day ago

    The only thing that could possibly save Java on the desktop would require Oracle not owning it.

    • pjmlp 18 hours ago

      Had it not been for Oracle, Java would have died in version 6.

  • misja111 17 hours ago

    The JRockit JVM had it already 20 years ago, and the company was even bought by Oracle shortly afterwards.

hn_submit 20 hours ago

A little late, isn't it? .NET has had this for over 20 years (pre-compile).

java-man 1 day ago

I hope at some point we'll get AOT-only mode (or compile to native) and maybe even cross-compilation.

  • invalidname 1 day ago

    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.

  • nirvdrum 1 day ago

    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

    • Twirrim 1 day ago

      The slight drawback is that AOT compiled Java code's performance isn't as good as the JIT compiled form running on the JVM.

      There's a reason why folks keep coming back to JIT compilation. It's hard to beat the value that can be gained from actually gathering data on how the code is actually used, which leads to a whole set of potential optimisations (which is the problem Profile Guided Optimisation attempts to solve for AOT compiled code.) The JVM is arguably one of the most advanced and capable JITing runtimes.

      As with anything it's a trade off. Fast start times, pretty fast running (I think maybe less memory usage?); vs the full JIT speed you can get on the JVM at the cost of start-up speed and memory consumption. All depends on what you want to use the application for.

      If you're talking something like a serverless function, go native. If you're talking production server where you're measuring runtime in more than dozens of minutes, probably better to stick to the JVM & JIT.

      • bebop 1 day ago

        Full agreement with everything you said. Just one detail to add on serverless (and potentially in a scheduled environment like k8s), startup time can be an order of magnitude or more faster in a native build. For instance, I have some quarkus applications that can take 10 seconds to ready running via the jre that take 10ms or less to start as a graalvm built binary.

        • silon42 17 hours ago

          I wish JVM could at least recover the memory after some time and drop at runtime what quarkus / AOT drops at build time.

          • samus 13 hours ago

            It can and already does. It mostly depends on the GC. Or do you mean non-heap memory that the runtime uses?

      • pjmlp 18 hours ago

        Depends on how much effort, like on C and C++, you are willing to put into PGO metadata for the compiler and linker.

  • exabrial 1 day ago

    progress, not perfection! Java is on fire right now with new stuff landing. We'll get there!

  • layer8 1 day ago

    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.

    • java-man 1 day ago

      we are not talking about fully supporting dynamic loading, because it's not needed in all the cases, and in some cases, the list of allowed classes in the application can (or must) be limited.

      i guess what i am saying is that AOT-only case is not "standard java", but something that will make a (more) useful replacement for things like c/c++.

      • samus 13 hours ago

        > i guess what i am saying is that AOT-only case is not "standard java", but something that will make a (more) useful replacement for things like c/c++.

        What is the objective here? Startup latency or throughput? Both will be vastly improved by JEP 544.

    • jasomill 1 day ago

      It works pretty well for greenfield projects on .NET, so long as you can live without third-party libraries that don't support AOT.

      Existing code bases can be a pain, though, especially applications that heavily rely on things like C++/CLI and COM Interop that basically need to be rewritten from scratch.

      Which is not to say that it's intended to replace the traditional JIT runtime in applications where it isn't troublesome, because it's not.

      • layer8 1 day ago

        > so long as you can live without third-party libraries that don't support AOT.

        Yes, and that’s a significant limitation in the Java library and framework ecosystem.

        • Rohansi 1 day ago

          But the possibility of AOT compilation is what drives libraries to support it. That's why .NET libraries are adapting over time to support AOT.

  • samus 13 hours ago

    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.

  • za3faran 12 hours ago

    GraalVM native-image already provides an AOT-only executable.

stevefan1999 20 hours ago

Welcome to ngen and GAC, Java

  • pjmlp 18 hours ago

    Only as free beer, Java has had similar commercial offerings before .NET was created out of J++ lawsuit, like Excelsior JET.

    • jayd16 11 hours ago

      The .NET implementation also has a similar AOTCache with ReadyToRun. That's a public and old enough to have pros and cons shake out in the real world that would be worth talking about.

      I'm not sure the pissing contest is really relevant, nor do I think Excelsior JET is all that common but if that is a similar system and you would like to talk about the real world impact please expand on it.

      https://learn.microsoft.com/en-us/dotnet/core/deploying/read...

ledo9915 1 day ago

Data point from a small side project: I run user code in a Piston sandbox for a coding challenge site. A trivial Java program costs 2.5-3 s wall clock on a 4 vCPU box, almost all of it javac plus JVM startup. Go takes about 1.7 s for build plus run, and Swift running through the interpreter is at 0.4 s. Java is the reason I had to put a global rate limit on compiled languages at all. If AOT gets the JVM side down to a few hundred ms, that changes what a small box can serve.

  • xxs 17 hours ago

    if that's any help: (guessing) you have tons of dependencies in multiple jars, if you bundle them all in a single jar (i'd not even compress it), the startup time would massively decrease. Stuff like dependency injection, makes the issues worse.

    Lots of the strautup time is class resolution/loading. With that being said - some time last year I ended up optimizing on the main application servers developers run with startup time being over 90s (and often times close to 3min), down to 11seconds (still tons of access of database configurations, secrets, discoveries, and what not). One of the main optimizations was runtime build up of a single jar with the classes being loaded, and then concurrently verifying the classes/resources have not changed.

  • samus 13 hours ago

    How much of these 3s is javac? JEP 544 will only help reduce javac's overhead, not that of the compiled program.

SillyUsername 1 day ago

So we've gone full circle again?

I suppose write once run anywhere is no longer a goal either.

"It is not a goal to support all CPU architectures currently supported by HotSpot."

This pretty much validates the point of view that VM design is now baggage, as a sandbox it has been flawed, for performance it's been prohibitive, and cross platform portability by virtue of being virtual, was just a convenient byproduct.

AI now handles the portability, sandbox security hasn't changed (cf. docker still has sandbox problems, LLMs have sandbox problems, it's always an ongoing concern) and that just leaves Java, as always, chasing performance.

  • nirvdrum 1 day ago

    The very next sentence after the one you posted is "We expect normal porting activities to eventually add support for all major architectures." Goals and non-goals inform the scope of the proposal. Given this is effectively an "add-on", I don't think it's unreasonable to prioritize the most popular platforms. Others will continue to execute Java perfectly fine in JVM mode.

  • randomUUID 1 day ago

    I don't get your point. This is what JEP says:

    - "Improve startup and warmup time by making optimized native code for an application instantly available when the HotSpot Java Virtual Machine starts."

    - "If the workload changes in production, regenerate native code dynamically for continued peak performance, providing the best of both ahead-of-time (AOT) and just-in-time (JIT) compilation."

    - "Ensure that shifting from AOT-compiled code to JIT-compiled code is invisible to applications."

    That is JEP544 doesn't substitute JVM and JIT optimizations. It just changes the nature of the JIT's outcome.

  • pjmlp 1 day ago

    Not at all, this is OpenJDK getting JIT cache feature like OpenJ9 has since 2008, or ART since Android 7.