I can't believe that after all these years Java still didn't fix their startup time.
I can't believe that after all these years Java still didn't fix their startup time.
[1]: E.g. see https://spring.io/blog/2023/10/16/runtime-efficiency-with-sp...
Im yet to reach the X-men level super qualities that can detect, and work in 40 ms chunks. Or at least even notice a 40 ms delays.
I envy the humans who can notice such small chunks of time.
git- I take more time to write the commit message to worry about 40 ms.
I mean sure optimise code to run it fast. But its not something that a human notices.
Isn't the initialization procedure (or at least the vast majority of it) exactly the same at each run ?
That said, there are alternative runtimes that have different tradeoffs. For example, Babashka is a runtime for Clojure that uses GraalVM instead of the JVM as the foundation. Babashka scripts have about a 10ms startup time on my M1 MacBook Air.
In some sense it's Clojure's fault for having an implementation that causes slow JVM startup, but it's also the JVM's fault that the way Clojure uses it causes it to take a long time to start.
So it is indeeed a Clojure issue, not a JVM one.
Also available since around 2000 from comercial vendors, of which, Aicas and PTC are the main survivors.
https://www.aicas.com/wp/products-services/jamaicavm/
https://www.ptc.com/en/products/developer-tools/perc
OpenJ9 also does JIT caching across executions, https://eclipse.dev/openj9/docs/aot/
OpenJDK also does caching but at higher level,
https://docs.oracle.com/en/java/javase/22/vm/class-data-shar...
https://wiki.openjdk.org/display/HotSpot/Application+Class+D...
Project Leyden plans to add a similar JIT cache like on OpenJ9, https://openjdk.org/projects/leyden/notes/02-shift-and-const...
Azul and OpenJ9 have cloud JIT servers, that share execution heuristics and dedicate servers for highly optimizing compilers,
https://www.azul.com/products/prime/cloud-native-compiler-fa...
https://eclipse.dev/openj9/docs/jitserver/
Finally, although technically not really Java nor JVM, the Android Runtime (ART), does a mix of high performance interpreter written in Assembly, JIT, AOT compilation, and PGO sharing across devices via Play Store (cloud profiles).
AppCDs in OpenJDK currently has terrible ergonomics. Clojure can't really offer it. Each user must go out of their way to leverage it. So you can't really release an app that automatically leverage it, the user needs to launch it with all the command incantations, etc. And it's so sensitive to class path changes, etc. It kind of sucks to be honest. But some people still use it for prod release, since you can set it up in a docker easily. But the use-case for fast startup are desktop apps, CLIs, scripts, etc. And for all those, AppCDs are super annoying to setup. See: https://ask.clojure.org/index.php/8353/can-we-use-appcds-to-...
Still, AppCDs don't fully solve the startup issue, because all the static initializations stuff takes a considerable amount of time, and that does not get cached by AppCDs.
But before Babashka, that was indeed a barrier to using Clojure in shell scripts. Now we have it all!
It's also possible to compile your JVM Clojure program yourself to a binary with GraalVM for even better performance than Babashka and even faster startup.
I feel a good fraction of code will dip into Java libs at least a bit - so you're limited in what libraries you can use
I think the real solution is probably Graal native - though it's not part of the official toolbox/deps.edn
Much more rarely — mostly when talking about writing CLI tools in a JVM language — people actually are complaining about the single second-or-so it takes the JVM to start up. (Usually because they want to run this tool in a loop under xargs(1) or find(1) or something.)
This last second of startup lag is (AFAIK) quite hard to improve, as it's mostly not the JVM itself starting up, but the static methods of JVM classes being called as those classes are loaded — which can do arbitrarily much stuff before any of your own code gets control. (Due to legacy code expecting to read certain per-boot-dynamic info as static fields rather than as the results of static method calls, I believe the JRE runtime is actually required to do quite a lot of that kind of static initialization, to pre-populate all those static fields, just in case something wants to read them.)
---
You'd think that GraalVM could inherently skip most of this, because the Graal compiler does dead-code analysis. "If nothing in your code reads one of those static fields, then nothing needs to write that field, so let's not invoke the static initializer for that field." But that's not true: static initializers are exported and called by the runtime — so they're always "alive" from the compiler's perspective. The Graal compiler isn't doing full-bore data-flow analysis to determine which static members of which classes are ever read from.
I believe GraalVM does try to work around static initializers as much as it can, by pre-evaluating and snapshotting as much of JVM runtime's static initializer code as possible by default, converting it all into const data structures embedded in the class files before the native codegen step gets run on it (see: https://www.graalvm.org/latest/reference-manual/native-image...).
It's not possible to apply this pre-baking to 100% of classes, sadly — some of these data structures need environment data like env-vars or system network config threaded into them on boot.
(I wonder anyone on the Graal project is working on a fully-general static-initializer optimization pass, that does something like concolic execution to bake these initializers out into either fullly-constant data if possible, or if not, then constant "data templates" plus trivial initializer functions that just gather up the few at-boot context fragments, shove them into the "data template" using a low-level hook, and then memcpy the result out onto the heap and call it an object.)
It's how much code you load.
E.g. any Python project has to deal with this, like Mercurial.
Java projects tend to (insert stereotype) have a lot of code.