[HN Gopher] Let's Take a Look at JEP 483: Ahead-of-Time Class Lo...
___________________________________________________________________
Let's Take a Look at JEP 483: Ahead-of-Time Class Loading and
Linking
Author : ingve
Score : 45 points
Date : 2025-03-28 11:11 UTC (11 hours ago)
(HTM) web link (www.morling.dev)
(TXT) w3m dump (www.morling.dev)
| robertlagrant wrote:
| > The AOT cache file has a size of 66 MB in this case. It is
| considered an implementation detail and as such is subject to
| change between Java versions.
|
| Does this mean this is mostly focused on containerised
| environments? I assume it means you couldn't upgrade the JVM on a
| server and know that this will keep working?
| gunnarmorling wrote:
| Leyden's AOT support is not specifically geared towards
| containerized workloads, AFAICS. It's a general trend in the
| Java world though to bundle the JVM alongside applications,
| potentially creating a tailored JVM distribution with just a
| subset of all the available modules of the SDK. The model where
| one JVM is installed on a server and multiple applications are
| using it is becoming more and more rare. In that current model,
| a JVM upgrade would happen when distributing a new version of
| the application using it.
| ignoramous wrote:
| > _While start-up times don 't matter that much for long running
| workloads, they can make a huge difference in cloud-native
| scenarios where applications are dynamically scaled out, spinning
| up new instances on demand as the load of incoming requests
| increases._
|
| "App Images" are what these are called in Android afaik. ART (the
| Android Run Time) has been capable of "AoT Class Loading &
| Linking" since 2016/7 (and ~pauseless GC since 2019). Seems like
| the desktop/server grade JVMs have a _lot_ of ground to cover.
| pjmlp wrote:
| On the contrary, lots of this work was already present in
| commercial products long before Android came to be,
|
| https://www.ptc.com/en/products/developer-tools/perc
|
| https://www.aicas.com/products-services/jamaicavm/
|
| https://eclipse.dev/openj9/docs/aot/
|
| https://www.ibm.com/docs/en/sdk-java-technology/8?topic=refe...
|
| Now gone, https://en.wikipedia.org/wiki/Excelsior_JET
|
| What you are getting now is commercial work being offered as
| free beer, also why Excelsior JET is no longer relevant as
| company.
| ignoramous wrote:
| Thanks.
|
| > _On the contrary, lots of this work was already present in
| commercial products_
|
| I should have been clearer that I meant other OSS JVMs have
| catching up to do.
|
| > _long before Android_
|
| True. The evolution of Dalvik/ART remind me of _HP Dynamo_ :
| https://archive.arstechnica.com/reviews/1q00/dynamo/dynamo-2.
| ..
|
| > _What you are getting now is commercial work being offered
| as free beer_
|
| ... AOSP is free as in beer.
| pjmlp wrote:
| > AOSP is free as in beer.
|
| True, however it isn't Java proper, and apparently Google
| has started to rewrite subsystems in Kotlin as well, when
| initially they said the OS layers would still be Java
| (language).
|
| Apparently the updates to Java 11 and 17 LTS subsets in
| ART, were motivated to keep up with Maven Central more than
| anything else.
| pron wrote:
| Both the requirements and the workload challenges of server-
| side applications differ greatly from client applications. The
| JDK has had a pauseless GC (i.e. <1ms pauses) for 18 months
| now. The need for fast startup and warmup in server-side apps
| is newer than for client apps, and even with it, optimising for
| peak performance is still a higher priority than for client
| apps.
|
| You should expect a runtime that is used more on the server
| side to be "behind" when it comes to concerns that are more
| prevalent on the client and vice-versa, and that's exactly what
| you see.
| kbolino wrote:
| I was configuring Shenandoah GC on OpenJDK 8 before COVID, so
| I can say for sure that it's been _available_ for a lot
| longer than 18 months. However, ZGC (the other low-pause-time
| collector) has only been the _default_ for a little while.
| pebal wrote:
| Please don't write pauseless if there are short pauses.
| Pauseless in the Java GC context is a marketing scam.
| pron wrote:
| We call them low-latency GCs, but I was responding to a
| comment that called the same thing "pausless". However,
| even programs without any GC pauses can have pauses of a
| similar duration due to OS activity, so programs on regular
| OS aren't generally pauseless anyway and the term is mostly
| meaningless in most contexts. As a marketing term (which we
| don't use) it merely means the application won't experience
| GC-related pauses that are longer than OS-related pauses.
| pebal wrote:
| This is some weird way of counting. A system pause plus a
| GC pause is two pauses. Just because one pause can't be
| avoided doesn't mean you can introduce more pauses.
| pron wrote:
| Just to be clear, the pause in ZGC is done as an
| optimisation of thread synchronisation (there's no GC
| work done in the pause). It's just as easy to do things
| without anything you would consider a pause, only it's
| less efficient. Momentarily stopping all program threads
| is just an efficient way to deliver a message to all
| threads considering that the OS might have some threads
| descheduled.
|
| At the end of the day, we care about the latency for some
| particular thread(s), and truly minimising that at this
| level requires a realtime kernel (that comes with
| significant performance disadvantages).
| ignoramous wrote:
| > _I was responding to a comment that called the same
| thing "pausless"_
|
| In my defense, I called it "~pauseless" (as in, _roughly_
| pauseless).
| vips7L wrote:
| Pebal is really pedantic about the term in every thread.
| Because apparently only he has ever achieved pause free
| GC for his C++ gc.
| gunnarmorling wrote:
| Unless you are on a realtime OS, you may see longer pauses
| from the OS due to scheduling other processes, so I think
| it's fair to consider the new low-latency collectors
| (Shenandoah, ZGC) pauseless from a practical point of view.
| pebal wrote:
| You can't call GC pauseless if it introduces pauses. We
| don't say something is free if you have to pay little for
| it. We say it's cheap.
| adambatkin wrote:
| I think as a technical term, pausless (as used here) has
| a specific meaning, just like real-time has a specific
| technical meaning (not literally 0 latency since that's
| generally physically impossible).
| ignoramous wrote:
| > _You should expect a runtime that is used more on the
| server side to be "behind" when it comes to concerns that are
| more prevalent on the client_
|
| True, but the Client x Server concerns seem to be overlapping
| now, across the board. For instance, tech built for ChromeOS
| (a desktop class OS) is also used by GCP ("Container-
| optimized OS") and AWS ("Firecracker").
|
| While ART has had tiered compilation (C1/C2 in the OpenJDK
| world) for quite a while. I also mentioned ART has long had
| _Copying Collector_ (almost pauseless GC), which could help
| Server-grade apps just the same.
|
| All that said, for ART, things are relatively "simpler" as it
| has to only target Linux+Fuschia on ARM32/64, x86/amd64,
| RISC-V ISAs.
|
| An interesting side note about Dalvik/ART (Zygote) is that it
| uses the old Server-side technique of forking (but without
| exec; like httpd's "fork mode") to share pages and resources
| with its children (ie, with _all_ app processes on Android).
| pron wrote:
| > True, but the Client x Server concerns seem to be
| overlapping now, across the board.
|
| Sure, which is why you're seeing this work.
|
| > While ART has had tiered compilation (C1/C2 in the
| OpenJDK world) for quite a while. I also mentioned ART has
| long had Copying Collector (almost pauseless GC), which
| could help Server-grade apps just the same.
|
| I hope you're not suggesting that ART has a similar quality
| of compiler optimisations and GC as the JDK. But if you
| are, you're welcome to try and run significant server
| workloads on ART and see how they compare to JDK 24.
|
| BTW, the last (Go-style) non-copying collector was
| deprecated in the JDK in 2017 and completely removed in
| 2020: https://openjdk.org/jeps/363. The reason why the low-
| latency GCs (what you call "almost pauseless") aren't the
| default in the JDK is that minimising latency is not (yet?)
| the top requirement for server apps. Some value throughput
| more.
| cleverfoo wrote:
| I think the big problem here is conceptual. The JDK folks are
| looking at this akin to PGO when, IMHO, they should be looking at
| this as an AOT cache (yes, the flag names make this even more
| confusing). How do those two differ, you ask?
|
| With PGO you do a lot of deliberate work to profile your
| application under different conditions and feed that information
| back to the compiler to make better branch/inlining decisions.
| With a AOT cache, you do nothing up front, and the JVM should
| just dump a big cache to disk every time it exits just in case it
| gets stared again on the same host. In this case, training runs
| would just be a" run you did to create the cache". With that
| said, the big technical challenge right ow is that building the
| AOT cache is expensive hence performance impacting and cannot
| really be done alongside a live application - but that's where I
| think the focus should be, making filling the aot cache something
| less intensive and automatic.
|
| Another aspect this strategy would help with is "what to do with
| these big AOT cache files", if the AOT cache really starts
| caching every compiled method, it will become essentially another
| so file possibly of a size greater than the original JAR it
| started off with. Keeping this is in a docker image will double
| the size of the image slowing down deployments. Alternatively,
| with the aot cache concept, you just need to ensure there is some
| form of persistent disk cache across your hosts. The same logic
| also significantly helps CLIs, where I dont' want to ship a 100MB
| CLI + Jlink bundle and have to add another 50MB of aot cache in
| it - what I do want is every time the client uses my CLI the JVM
| keeps improving the AOT cache.
| jagged-chisel wrote:
| Is there any reason to think Java code can't be statically
| linked, and then dead code eliminated (for that specific build
| of the app)?
|
| I'm not asking if the tooling currently exists, I'm curious if
| there's something inherent in .class files that would prevent
| static linking.
| pron wrote:
| Native Image does exactly that already, but producing an AOT-
| compiled-and-linked native executable is not the goal, it's
| just a means to some goal. The real question is what is it
| that you want to optimise? Is it startup/warmup time? Is it
| the size of the binary? Peak performance? Developer
| productivity? Program functionality? Ops capabilities?
|
| AOT compilation certainly doesn't give you the best outcome
| for all of these concerns (regardless of the language).
| cleverfoo wrote:
| Well, assuming that by "statically linking" you mean in the c
| sense, that's exactly what GraalVM native image does today,
| it statically analyzes the JAR for reachability only
| compiling the methods/classes in use. This works but it's
| also what makes native-image difficult to use and brittle.
|
| It's hard, and some might argue impossible, to statically
| analyze reachability in a dynamic language like java that
| allows for runtime class loading and redefinition. As it
| turns out, Java is much closer to javascript than C++ in
| terms of dynamic runtime behavior.
| vips7L wrote:
| In the Java sense, if you properly utilize JPMS, JLink can
| cut dead modules and reduce your image size drastically.
| This obviously of course like you said depends on how
| "open" your runtime model is. If you're not dynamically
| loading jars it works really well.
| cogman10 wrote:
| > I'm not asking if the tooling currently exists, I'm curious
| if there's something inherent in .class files that would
| prevent static linking.
|
| It's not so much a problem with the .class files, instead
| it's a problem with reflection.
|
| I can write `var foo = Class.forName("foo.bar.Baz")` which
| will cause the current class loader to look up and initialize
| the `foo.bar.Baz` class if it's available. I can then
| reflectively initialize an instance of that class by calling
| `foo.newInstance()`
|
| Java has a ton of really neat meta-programming capabilities
| (and those will increase with the new ClassFile api).
| Unfortunately, those make static compilation and dead code
| elimination particularly hard. Tools that allow for static
| compilation (like graal) basically push the dev to declare
| upfront which classes will be accessed via reflection.
| robertlagrant wrote:
| It would be nice to be able to trigger AOT somehow, e.g. as
| part of a Docker build, or as part of an app startup as you
| say. Then the software deployment can decide what to do.
| pron wrote:
| The Leyden team are looking to do exactly what you're looking
| for. There will be further JEPs.
___________________________________________________________________
(page generated 2025-03-28 23:02 UTC)