"Almost every Java program, library, and framework uses some degree of reflection and dynamic classloading, and so you do have to spend the effort to configure Graal appropriately." -- this is by far the biggest problem with native binary compilation.
Imagine spending 15+ minutes building an exe, and it stopping at minute 15 because some things needed to be added in the config. Or, after waiting 30 minutes to have the exe ready, it refused to run because of the same problem.
The issue is we don't know what we don't know. You don't "forget to include something" because you don't know what to include (and even after seeing the error, you still don't lol).
This has been a solved problem since Exclesior JET in the 2000's, via agents and PGO, nowadays sadly gone because they no longer can compete with free beer GrallVM and OpenJ9.
Also ART takes the best of both worlds, as they quickly learned the best is to have JIT/AOT compiler collaborating, thus AOT gets all the reflection related info from the JIT, or Play Store Baseline Profiles and has all it needs to produce a proper AOT binary with the relevant execution flows.
To note that Java and .NET to some extent are catching up to Eiffel, Oberon, Smalltalk and Common Lisp toolchains, of a mix JIT/AOT, while inovating on their own ways as well.
(it is part of the graalvm so use that as your jvm to do this)
pjmlp 150 days ago [-]
Indeed.
killingtime74 152 days ago [-]
We've been using it at work for a few years. A cli tool, builds in about 6 minutes. We compile it to Linux, Mac ARM and Mac Intel. You're correct about configuring libraries but I found those to be the minority. Most work without configuration. I do this because I will not use Golang if given a choice and Rust is not allowed.
sgammon 150 days ago [-]
Builds have been slow for a long time via Native Image, but make sure you upgrade to the latest because it has gotten much faster lately. They've also worked hard on binary size.
A pure Java hello world now compiles in a few seconds on a commodity machine and weighs in at about 9mb.
Also, make sure you enable `-Ob` on newer versions of GraalVM, which significantly speeds up build times.
gf000 148 days ago [-]
I would only use -Ob for development, it's not for production builds.
nomercy400 150 days ago [-]
How do you compile to multiple platforms? Do you have separate machines for each platform?
Do you use any cli library in front?
Is 12-20mb acceptable for a cli executable?
Working on a cli tool poc for work here, hence the interest.
killingtime74 149 days ago [-]
Like the other poster, we have to compile it separately on different architecture machines (we are on Bitbucket). We use Quarkus as the base and picocli as the cli https://quarkus.io/guides/picocli. Quarkus takes care of a lot and makes the native image experience nicer. Size wise, for internal use our users don't complain, since we are all devs.
I am on the same boat, JVM, .NET and nodejs ecosystems before Go.
So many languages, tools and libraries to chose from, without the "simple minds" culture, even C manages to be more feature rich.
Jabihjo 152 days ago [-]
What's your issue with Golang? (Not hating, just curious)
killingtime74 151 days ago [-]
(sorry it's probably it an unpopular opinion) the error handling is hard to read, they purposely didn't incorporate any syntax sugars and innovations of previous years. They were one of the first popular languages with go routines although Project Loom in Java will soon have preemptive multi threading as well.
tannhaeuser 150 days ago [-]
I guess Quarkus and Spring 3 native are meant for bringing existing apps to serverless deployments and fast startup times (plus process-level isolation as an option?), but it's true there are also new apps being developed using Quarkus. The investment in tooling, inertia of the ecosystem of third-party libs, and predominance of traditional JIT over AOT deployments must be the reason why they're not just doing away with Spring's reflection in the first place.
sgammon 150 days ago [-]
Micronaut is essentially Spring anyway, but with build-time codegen instead of classpath scanning and runtime annotation processing.
It even has a compat layer, and like Quarkus it supports GraalVM out of the box, with minimal reflection at runtime.
I've built several apps that compile to binary across multiple platforms. The ecosystem is quite mature now, especially if you start from scratch and use things like Quarkus, Helidon or Springboot 3.x. There are *very few* libraries that might need special config.
Much of this configuration could be automatic if more attention were given to GraalVM by library authors.
littlecranky67 150 days ago [-]
""Almost every Java program, library, and framework uses some degree of reflection and dynamic classloading" - The .NET framework faced the same issues, and much of their ahead-of-time compilation efforts were removing all those API calls to reflection/introspection calls from the base class library. Years of effort and it is still not 100% there yet - I wouldn't count that this is a lot easier in Javaland.
jojohohanon 151 days ago [-]
So springboot and injection does a lot of this; searching the list of known classes for a suitable candidate for injection.
But it’s dynamic by implementation only; to alleviate what came before which was a tedious manual enumeration in xml of those same discoveries.
Presumably we could ask springboot’s discovery to output exactly those finding and the remove the introspection.
But to be clear, tedious manual XML configuration was also Spring. Others may have done it earlier, but Spring still decided it was the way to go, put their name on it, and released it.
sgammon 150 days ago [-]
You have described Micronaut
p2detar 153 days ago [-]
> Graal can only create native binaries targeting the system on which it is running. That means that if you want to create binaries for {Linux,Windows,Mac}x{Intel,ARM}, you need 6 different machines in order to build the 6 binaries and somehow aggregate them together for publication or deployment. This is not a blocker, but can definitely be inconvenient v.s. some other toolchains which allow you to build native binaries for all targets on a single machine.
To me this is a huge downside. One of the reasons I like Go so much is that I can build binaries for any platform I want to support on my Mac or with Docker.
rixed 150 days ago [-]
GCJ could do that, as well as JET I believe, but both are long dead.
The history of native compilation for java I find fascinating: Nothing looks like a normal, boring programming language as much as java, yet somehow it resists being treated as a normal, boring programming language, and needs its own galaxy of special idioms and tools, like it was some smalltalk or lisp.
chii 150 days ago [-]
because java is not designed for it. It's retrofitted in, because of trends.
There's actually, imho, no need for native compilation for java tbh. Why isn't shipping the runtime with the code a more common practise? it's how intellij or eclipse worked. People today ship electron apps for god sakes!
If you _need_ native, for one reason or another and it's non-negotiable, then choose a native compiled language.
p2detar 150 days ago [-]
> Why isn't shipping the runtime with the code a more common practise?
That's what I actually do for our product. The JRE is shipped with it as well as startup scripts and services registration bridge for Windows.
I guess the native compile thing is more for "Java on the Cloud" which tbh is not where Java shines. I say this as a long-term Java developer. Java is great for monoliths - on premises customers are supper happy. Java on the cloud seems like a waste of resources to me.
sgammon 150 days ago [-]
> Why isn't shipping the runtime with the code a more common practise?
It really shouldn't be. It bloats everything. Containers are bigger. Software is bigger. There's no reason to do this, since very few apps use the entire surface area of a runtime (Python, Node, Java, etc).
Instead, a native GraalVM binary can embed all these things, including only the portions which the application needs to run. Thus it behaves like a dynamic piece of software during development, but a static, high-performance, low-surface-area-of-vulnerability native binary in production.
> That's what I actually do for our product. The JRE is shipped with it as well as startup scripts
Please, as a user, I implore you to look into native targets. It's not as hard as it seems anymore. Some thins really do need Jlink.
> I guess the native compile thing is more for "Java on the Cloud"
Not so. GraalVM is great for embedded development. It's great for desktop development or even shared native library development.
> Java on the cloud seems like a waste of resources to me.
There is no difference now between C++ "in the cloud" and Java "in the cloud" except Java remains memory safe by default.
p2detar 150 days ago [-]
> Please, as a user, I implore you to look into native targets. It's not as hard as it seems anymore. Some thins really do need Jlink.
You are not my target. It's enterprise software - I'm given a Windows Server VM and the rights to install our software, that's it. Most IT admins don't even want to bother to install or let alone support JRE updates. We got tons of customers and this experience is uniform.
> It's great for desktop development
For desktop I'd totally use Jlink. Really the only possible place GraalVM's NI seems like a plausible fit to me is CLI tools and Java Microservices, where fast start and low memory consumption actually make sense. edit: But honestly at this point for Microservices I'd probably go with Go.
xxs 150 days ago [-]
While not a fan of the cloud - in case of java; package it in docker or whatever deployables along with the JRE (or even JDK) and call it a day.
Compile to native makes sense only for very fast startup cases.
As for wasted resources - there are cases where the servers have to be deployed in specific jurisdictions (compliance) - it's easier to use AWS than manage tons of small sites. Still I don't see any need for native compilation.
sgammon 150 days ago [-]
> Compile to native makes sense only for very fast startup cases.
It also makes sense to reduce container size and vulnerability surface, and to reduce warmup time to peak JIT performance.
> Still I don't see any need for native compilation.
Sure, there isn't a "need," per se, just like there is not a "need" for JIT. This is how technology evolves. Things get better. Java's startup and time-to-peak performance are now comparable to native langs.
xxs 150 days ago [-]
>and to reduce warmup time to peak JIT performance.
This actually works in the opposite way - JIT is a guided compilation which uses the input for best results. There are many optimizations done for multi call sites, e.g. static linking + guard, dual site inline, inline caches, and the v-table calls if all fails. Without guided optimization such decisions are not likely to happen.
In other words for peak runtime performance JIT would surpass ahead of time/native compilation.
>reduce container size
I would be exceptionally hard pressed to find a single case where that would matter (aside specific client deployments) - server side and the like I'd not care about extra 100MB or so deployable. E.g. if I want really small footprint I'd go and do it all in pure C w/ an absolute min external library support.
>startup times
I already mentioned it - and anything not sub 0.5sec, Java does quite ok. There is a lot to be said about the use of DI frameworks with blatant class path scans, but that's not necessarily Java's fault.
sgammon 149 days ago [-]
Peak runtime performance is slightly better on JVM, yes. Native Image ships a JIT within your binary so it is not as different as one might think.
chii 149 days ago [-]
my understanding is that native compile isn't shipping a jit, but is running the jitted code that was produced during compilation. However, if your code is so "dynamic" (aka, has lots of deeply nested inheritance) that it slows down procedure calls from so many levels of indirection, then a JVM runtime will jit a better version of native code when running using runtime information (such as jump directly to the correct virtual function instead of indirection). The compiled version do not have access to this runtime information, so you can't get this sort of optimization.
However, for "simple" code (like arithmetic, or manipulation of bits, arrays etc), i think it's pretty much the same.
p2detar 150 days ago [-]
> There is a lot to be said about the use of DI frameworks with blatant class path scans, but that's not necessarily Java's fault.
I used to support Atlassian's products on the intranet before we migrated (almost) everything on their cloud: Jira, Bitbucket, Bamboo, Confluence. I think Jira and Bitbucket were hands down the slowest of the bunch to boot and from what I could tell it was exactly due to classpath-scanning and god knows what else. I could never believe a 16 CPU 32 GB KVM needs from 3 to sometimes 5 minutes just to boot Jira. To me this was insane.
brabel 150 days ago [-]
Absolutely.
> Why isn't shipping the runtime with the code a more common practise?
Well, Java has jlink which is meant to do exactly that (ship the parts of the JVM you actually need, which is easy to figure out as there are tools to do that), and that's the recommended approach today for shipping anything Java.
sgammon 150 days ago [-]
> There's actually, imho, no need for native compilation for java
Sure, that is true, but with native compilation available, Java can do even more things. There are other reasons, too: startup time, quicker time to peak performance, and better interfacing with FFM and JNI, to name a few.
> If you _need_ native, for one reason or another and it's non-negotiable, then choose a native compiled language.
You could do that, sure. But then you would need to worry about all the things that come with that. For example, memory safety -- GraalVM preserves memory safety guarantees with native binaries. Even binaries which themselves ship JIT features.
weinzierl 150 days ago [-]
"Why isn't shipping the runtime with the code a more common practise?"
I'd say it is a pretty common thing. I can't tell you why it isn't the norm nowadays but it used to be that Sun and Oracle prohibited modifications to the JRE in their license. So you had ship the whole bloated thing or nothing to be compliant, even if it was technically not too hard to strip the JRE down to only the things your app needed.
pjmlp 150 days ago [-]
Actually one of the few production deployments for GCJ used to be Red-Hat AOT compiling Eclipse.
It wasn't retroffied, rather only available behind a paywall, that is what kept companies like Excelsior JET in business.
OpenJ9 from IBM traces its roots back to Java AOT compilation to embedded development, used to be called WebSphere Real Time.
PTC and Aicas are two companies specialized in real time JVM implementations for embedded development, with military and factory automation as main customer targets, also supporting AOT compilation for decades.
papichulo2023 149 days ago [-]
Isnt v8 smaller than the JVM?
chii 149 days ago [-]
v8 with chrome as the shell is not smaller than the jvm. So electron is comparable to the jvm in terms of shipping a product imho.
lmz 150 days ago [-]
Once you have reflection that's basically eval() so just LISP with worse ergonomics.
josefx 149 days ago [-]
> GCJ could do that
Was it actually any good? My only experience with it was Debian using it as default Java interpreter, which meant that any attempt to execute Java without first removing it resulted in untold amounts of suffering.
rixed 149 days ago [-]
My understanding is that its main merit was to remove the need to have the non-free Sun then Oracle Jdk installed. Aparently its technical merits were not sufficient to maintaint it in a post OpenJdk world.
jeswin 150 days ago [-]
> To me this is a huge downside. One of the reasons I like Go
C# with NativeAOT might be an easier path for Java developers. Some might like the expressiveness as well over Go.
pragmatick 150 days ago [-]
I don't think you'd be a Java developer anymore.
pjmlp 150 days ago [-]
Why not?
Both ecosystems complement themselves, some stuff Java ecosystem does much better than .NET, like multiple implementations, with various kinds of GC and JIT implementations, wider support of hardware deployments, tooling like Graal, industry standards, IDE implementations, a mobile OS,...
Other things the .NET ecosystem does better, support for value types, low level programming, SIMD, desktop development, game engines.
To this day they keep copying features from each other, and if either C# or Java isn't one's taste, there are still several other options on each platform.
Hence why I am confortably at home using them both, complemented by JS/TS for FE, and C++ for fiddling with their runtimes, or plugging into native libraries.
neonsunset 149 days ago [-]
What features is C# copying from Java?
pjmlp 149 days ago [-]
Most of C# 1.0 for starters, given that it was born from the lawsuit and J++ extensions, J/Direct, COM interop, events and Windows Foundation Classes.
More recently, default interface methods, apparently the ongoing discriminate union design seems to now be settling on similar Scala/Java approach, I still have to spend some time delving into it.
Not delving now on the CLR, versus the various JVM implementations, regarding GC, JIT, PGO, tiered compilation, escape analysis,...
149 days ago [-]
neonsunset 150 days ago [-]
That made me laugh out loud for some reason haha
Indeed!
brabel 150 days ago [-]
I've been using Dart instead. Not too far from Java, but can compile to executable without any cerimony.
josefx 149 days ago [-]
How is the cross platform UI situation on C#? Anything official?
neonsunset 149 days ago [-]
There is a good selection of multi-platform GUI frameworks: AvaloniaUI, Uno Platform, MAUI and smaller more focused alternatives.
pragmatick 150 days ago [-]
While I agree I think most use cases aren't for releasing end-user software that needs to run on many architectures and OSes but rather building enterprise software for one certain combination, usually x86 and linux, to deploy on a server or containerized in a hyperscaler.
But to contradict myself, I actually use windows, WSL and oracle cloud to build for windows x86, linux x86 and linux ARM64 to build a java desktop application. Of course I'd prefer it if I could just use my main machine and OS to build all those like Go can.
sgammon 150 days ago [-]
Also, I don't believe this is fully true. GraalVM depends on the underlying C compiler to generate native code, so if properly configured with target triples and flags (just like any cross-compiling setup) it should work.
gf000 148 days ago [-]
GraalVM reuses the normal java class loading logic which is platform-dependent. So e.g. on windows it will load some windows-specific File subclasses and the like, so this is more of an architectural problem to a certain degree.
(It really does loadClass everything reachable based on the code to be compiled)
taurknaut 150 days ago [-]
Agreed, this is definitely an odd and very unnecessary constraint on builds.
sgammon 150 days ago [-]
Well, it's also not true. But aside from that, plenty of software builds in its native environment. Cross-compiling is straight up hard for many toolchains.
cratermoon 150 days ago [-]
> this is a huge downside.
Also the compiler can only do static analysis and code generation. I'd much rather a hotspot VM analyze the running code and identify the optimizations on the fly.
Yasuraka 150 days ago [-]
>I'd much rather a hotspot VM analyze the running code
wasting gigabytes of ram and fast startup times
>and identify the optimizations on the fly
and still get gapped by statically compiled languages.
I guess these are a cheap price to pay for the benefit of ... having to install and patch runtimes.
pragmatick 150 days ago [-]
Native Image requires so much more work to have a medium-sized java project properly run when compiled the price gets a lot higher.
sgammon 150 days ago [-]
More of this configuration is automatic with every release
roncesvalles 150 days ago [-]
There are instances where JVM's dynamically optimized code runs faster than equivalent C code compiled to a native binary. The Hotspot JVM is no joke.
sgammon 150 days ago [-]
It's not either/or. Compiling a Native Image binary gives you the JIT power of the JVM, but with faster warmup time.
Peak-peak performance is not as good as JVM in many cases, but how long does your process actually spend within that space? Native Image can be optimal for many cases
forty 150 days ago [-]
> still get gapped by statically compiled languages.
Which statically complicated languages are you referring to? :)
gf000 150 days ago [-]
Unused RAM is wasted. If you have plenty available then doing useless memory releases (including dropping/destroying objects with RAII, which can end up doing a lot of work in case of a data structure containing other objects that have to be dropped) will just hurt your throughput. Nonetheless, Java has a pretty trivial single flag to set an appropriate heap size if you are not happy with the default.
--
You are thinking of AOT compiled, but does that AOT language support arbitrary class loading to expand functionality, live observability while taking almost zero overhead for that?
--
So what do you do if a statically linked library of yours has a vulnerability?
tannhaeuser 150 days ago [-]
What's your point? This is what the regular JVM/HotSpot has been doing for ages (and GraalVM, too). Whereas native-image does AOT, with optional profile-guided optimization to identify hotspots on the fly.
cratermoon 149 days ago [-]
Apparently I left out a line break. It should read
> this is a huge downside.
The compiler can only do static analysis and code generation. I'd much rather a hotspot VM analyze the running code and identify the optimizations on the fly.
So I believe we're in agreement, that AOT imposes does limited optimizations compared to HotSpot JIT
I wonder if you could wrap this process in something like qemu for a cross-architecture build setup?
taraparo 150 days ago [-]
You can e. g. create ARM builds on x86 using qemu but the compilation takes ages.
ludovicianul 150 days ago [-]
You can do the same with GraalVM on Mac for Java.
150 days ago [-]
ltbarcly3 150 days ago [-]
You realize you can run Linux, Windows, Mac x intel, arm builds on docker right? Right on your Mac.
dkjaudyeqooe 150 days ago [-]
I'm under the impression that the Community Edition version of GraalVM is neutered by a limited garbage collector and some other limitations when producing native binaries.
Anyone know if this true?
sgammon 150 days ago [-]
This is partly true. CE has the "serial GC," yes, and no access to G1 GC by default. But Oracle GraalVM (formerly "GraalVM Enterprise Edition," which no longer exists) was released under a much less restrictive license:
So, yes, GraalVM CE is slightly nerfed (the performance difference across all EE features is like 30% maximum, at peak, in limited circumstances). You also do not need to use GraalVM CE in as many places anymore, and Oracle GraalVM with G1 is a free download. Many free projects like Pkl use the full-blown VM.
Serial GC is still very performant. It's great for things like command line tools which don't need a low-pause long-running GC like G1.
dkjaudyeqooe 149 days ago [-]
So the runtime and libraies incorporated into native applications compiled by Oracle GraalVM is freely licensed? I tried to find that information but it's all a sea of legalese.
sgammon 149 days ago [-]
Yes. Native Image is covered by this license IIRC
sabas123 150 days ago [-]
IIRC there base kit like garbage collector and runtime are all the same between community and enterprise. The biggest feature that comes to mind that is behind the enterprise-only wall (or at least used to be) is Profile Guided optimization.
quikoa 150 days ago [-]
Could Oracle neuter it more in an update to push the commercial edition?
theflyinghorse 152 days ago [-]
Mill looks like an interesting option to try all around.
152 days ago [-]
150 days ago [-]
150 days ago [-]
neonsunset 150 days ago [-]
dotnet publish /p:PublishAot=true
:)
pjmlp 150 days ago [-]
If the dependencies are written with AOT in mind, otherwise plenty of errors will show up.
Also it isn't that easy if any of GUI frameworks from Microsoft are being used.
Ah, and if using WinUI, one even has the additional requirement that only UI elements implemented with partial classes can be AOT compiled due to WinUI magic code.
Rohansi 149 days ago [-]
I don't know why anyone even uses the GUI frameworks from Microsoft these days. WinForms is only good if you need to throw something together quick. Everything else should be Avalonia (https://avaloniaui.net/). Cross platform and does support AOT.
pjmlp 149 days ago [-]
Good answer to ask Fortune 500 using in office apps, and IT that usually has the policy what matters comes from Microsoft on a full Visual Studio install.
I do agree Avalonia and Uno are great projects.
metaltyphoon 150 days ago [-]
> If the dependencies
There needs to be more context here. I think the BCL is fully annotated already. Many of MS libraries are too including gRPC.
pjmlp 150 days ago [-]
Except most companies are using much more than just BCL.
Your gRPC example is also quite interesting, I am yet to use it in anger, and never seen it being used at any of our .NET customers.
Meanwhile as mentioned, GUI frameworks aren't supported, WinUI requires partial classes and only kind of works, ASP.NET only if using minimal APIs, EF partially, and so on.
So it isn't really telling the whole story that "dotnet publish /p:PublishAot=true" works regardless of the .NET codebase being compiled.
Alifatisk 150 days ago [-]
Will this produce a cross-platform standalone binary?
diggan 150 days ago [-]
No, and neither will one invocation of GraalVM.
neonsunset 150 days ago [-]
It will be completely standalone. The same way it works with Rust, Go, etc. The binary size will be smaller than Go but bigger than Rust.
Portability is by definition not possible with natively compiled binaries however. It is a choice - either produce an assembly (still single-file, .NET can do this unlike JVM offerings) that is completely portable, or produce a self-contained executable targeted at a particular OS and ISA.
diggan 150 days ago [-]
> It will be completely standalone
Sure, but not cross-platform, which the previous comment seemed to indicate that GraalVM could, which it cannot. Both tools need to compile on the platform they target, compared to Go/Rust where you can target other platforms even though you're not compiling on them.
pjmlp 149 days ago [-]
Cross compilation is a toolchain feature, not a programming language capability.
As for GraalVM, it has plenty of modes, the AOT one that is being talked about here, is native image.
Which can also use a JAR file as input, so even though it doesn't yet support cross-compilation, you can use the JAR file as single source, and then have a couple of build environments in e.g. Github pipelines.
To the end user it doesn't matter as long as they get their shinny executable available, and it only has to be done once as a project template.
Also note that cross-compiling only really works without issues for whatever compiled language, if the toolchains have access to every single library from the target system on the host environment, and no dependencies FFI into other languages.
neonsunset 149 days ago [-]
I was talking about .NET's publish modes (NativeAOT, portable assembly, etc.).
You are also confusing what it means to produce a portable assembly with the ability to target across operating systems or CPU architectures.
(also, most native toolchains do not let you easily cross-compile, Go makes significant sacrifices to make it possible, as long as you have no dependencies which rely on C/C++, and there are many of those)
neuroelectron 150 days ago [-]
Java had a huge reflection exploit a long time ago and most places blacklisted that library as a result so not a big loss there.
neuroelectron 150 days ago [-]
Well, wow. Apparently people are still blindly using reflection. A lot of people, in fact. Have you thought about literally any other language to build services?
rf15 149 days ago [-]
It's less a problem of the language and more of popular frameworks using reflections excessively. (usually for no gain other than creating platforms that are not controlled by normal readable/tracable code flow but annotations)
Rendered at 02:44:15 GMT+0000 (Coordinated Universal Time) with Vercel.
Imagine spending 15+ minutes building an exe, and it stopping at minute 15 because some things needed to be added in the config. Or, after waiting 30 minutes to have the exe ready, it refused to run because of the same problem.
The issue is we don't know what we don't know. You don't "forget to include something" because you don't know what to include (and even after seeing the error, you still don't lol).
I just wished all 3rd party libraries put their "include this config to include my lib in your exe", just like OSGi manifest (https://www.ibm.com/docs/en/wasdtfe?topic=overview-osgi-bund...).
For example, an issue still open for almost 2 years: https://github.com/firebase/firebase-admin-java/issues/800
https://en.wikipedia.org/wiki/Excelsior_JET
Also ART takes the best of both worlds, as they quickly learned the best is to have JIT/AOT compiler collaborating, thus AOT gets all the reflection related info from the JIT, or Play Store Baseline Profiles and has all it needs to produce a proper AOT binary with the relevant execution flows.
To note that Java and .NET to some extent are catching up to Eiffel, Oberon, Smalltalk and Common Lisp toolchains, of a mix JIT/AOT, while inovating on their own ways as well.
https://eclipse.dev/openj9/docs/shrc/#aot-code-and-jit-data
Competing looks like starting with what other people are already doing and making it better.
But you do need to start/run your app once to get the agent to do its stuff.
https://www.graalvm.org/latest/reference-manual/native-image...
(it is part of the graalvm so use that as your jvm to do this)
A pure Java hello world now compiles in a few seconds on a commodity machine and weighs in at about 9mb.
Also, make sure you enable `-Ob` on newer versions of GraalVM, which significantly speeds up build times.
Do you use any cli library in front? Is 12-20mb acceptable for a cli executable?
Working on a cli tool poc for work here, hence the interest.
I think you can shrink the size with one of the optimisation levels https://www.graalvm.org/latest/reference-manual/native-image...
Cosmo/APE support would fix this, and GraalVM already ships with a Musl libc implementation, so it isn't very far off.
https://github.com/oracle/graal/issues/8350
So many languages, tools and libraries to chose from, without the "simple minds" culture, even C manages to be more feature rich.
It even has a compat layer, and like Quarkus it supports GraalVM out of the box, with minimal reflection at runtime.
https://micronaut-projects.github.io/micronaut-spring/5.9.0/...
Much of this configuration could be automatic if more attention were given to GraalVM by library authors.
But it’s dynamic by implementation only; to alleviate what came before which was a tedious manual enumeration in xml of those same discoveries.
Presumably we could ask springboot’s discovery to output exactly those finding and the remove the introspection.
To me this is a huge downside. One of the reasons I like Go so much is that I can build binaries for any platform I want to support on my Mac or with Docker.
The history of native compilation for java I find fascinating: Nothing looks like a normal, boring programming language as much as java, yet somehow it resists being treated as a normal, boring programming language, and needs its own galaxy of special idioms and tools, like it was some smalltalk or lisp.
There's actually, imho, no need for native compilation for java tbh. Why isn't shipping the runtime with the code a more common practise? it's how intellij or eclipse worked. People today ship electron apps for god sakes!
If you _need_ native, for one reason or another and it's non-negotiable, then choose a native compiled language.
That's what I actually do for our product. The JRE is shipped with it as well as startup scripts and services registration bridge for Windows.
I guess the native compile thing is more for "Java on the Cloud" which tbh is not where Java shines. I say this as a long-term Java developer. Java is great for monoliths - on premises customers are supper happy. Java on the cloud seems like a waste of resources to me.
It really shouldn't be. It bloats everything. Containers are bigger. Software is bigger. There's no reason to do this, since very few apps use the entire surface area of a runtime (Python, Node, Java, etc).
Instead, a native GraalVM binary can embed all these things, including only the portions which the application needs to run. Thus it behaves like a dynamic piece of software during development, but a static, high-performance, low-surface-area-of-vulnerability native binary in production.
> That's what I actually do for our product. The JRE is shipped with it as well as startup scripts
Please, as a user, I implore you to look into native targets. It's not as hard as it seems anymore. Some thins really do need Jlink.
> I guess the native compile thing is more for "Java on the Cloud"
Not so. GraalVM is great for embedded development. It's great for desktop development or even shared native library development.
> Java on the cloud seems like a waste of resources to me.
There is no difference now between C++ "in the cloud" and Java "in the cloud" except Java remains memory safe by default.
You are not my target. It's enterprise software - I'm given a Windows Server VM and the rights to install our software, that's it. Most IT admins don't even want to bother to install or let alone support JRE updates. We got tons of customers and this experience is uniform.
> It's great for desktop development
For desktop I'd totally use Jlink. Really the only possible place GraalVM's NI seems like a plausible fit to me is CLI tools and Java Microservices, where fast start and low memory consumption actually make sense. edit: But honestly at this point for Microservices I'd probably go with Go.
Compile to native makes sense only for very fast startup cases.
As for wasted resources - there are cases where the servers have to be deployed in specific jurisdictions (compliance) - it's easier to use AWS than manage tons of small sites. Still I don't see any need for native compilation.
It also makes sense to reduce container size and vulnerability surface, and to reduce warmup time to peak JIT performance.
> Still I don't see any need for native compilation.
Sure, there isn't a "need," per se, just like there is not a "need" for JIT. This is how technology evolves. Things get better. Java's startup and time-to-peak performance are now comparable to native langs.
This actually works in the opposite way - JIT is a guided compilation which uses the input for best results. There are many optimizations done for multi call sites, e.g. static linking + guard, dual site inline, inline caches, and the v-table calls if all fails. Without guided optimization such decisions are not likely to happen.
In other words for peak runtime performance JIT would surpass ahead of time/native compilation.
>reduce container size
I would be exceptionally hard pressed to find a single case where that would matter (aside specific client deployments) - server side and the like I'd not care about extra 100MB or so deployable. E.g. if I want really small footprint I'd go and do it all in pure C w/ an absolute min external library support.
>startup times
I already mentioned it - and anything not sub 0.5sec, Java does quite ok. There is a lot to be said about the use of DI frameworks with blatant class path scans, but that's not necessarily Java's fault.
However, for "simple" code (like arithmetic, or manipulation of bits, arrays etc), i think it's pretty much the same.
I used to support Atlassian's products on the intranet before we migrated (almost) everything on their cloud: Jira, Bitbucket, Bamboo, Confluence. I think Jira and Bitbucket were hands down the slowest of the bunch to boot and from what I could tell it was exactly due to classpath-scanning and god knows what else. I could never believe a 16 CPU 32 GB KVM needs from 3 to sometimes 5 minutes just to boot Jira. To me this was insane.
> Why isn't shipping the runtime with the code a more common practise?
Well, Java has jlink which is meant to do exactly that (ship the parts of the JVM you actually need, which is easy to figure out as there are tools to do that), and that's the recommended approach today for shipping anything Java.
Sure, that is true, but with native compilation available, Java can do even more things. There are other reasons, too: startup time, quicker time to peak performance, and better interfacing with FFM and JNI, to name a few.
> If you _need_ native, for one reason or another and it's non-negotiable, then choose a native compiled language.
You could do that, sure. But then you would need to worry about all the things that come with that. For example, memory safety -- GraalVM preserves memory safety guarantees with native binaries. Even binaries which themselves ship JIT features.
I'd say it is a pretty common thing. I can't tell you why it isn't the norm nowadays but it used to be that Sun and Oracle prohibited modifications to the JRE in their license. So you had ship the whole bloated thing or nothing to be compliant, even if it was technically not too hard to strip the JRE down to only the things your app needed.
It wasn't retroffied, rather only available behind a paywall, that is what kept companies like Excelsior JET in business.
OpenJ9 from IBM traces its roots back to Java AOT compilation to embedded development, used to be called WebSphere Real Time.
PTC and Aicas are two companies specialized in real time JVM implementations for embedded development, with military and factory automation as main customer targets, also supporting AOT compilation for decades.
Was it actually any good? My only experience with it was Debian using it as default Java interpreter, which meant that any attempt to execute Java without first removing it resulted in untold amounts of suffering.
C# with NativeAOT might be an easier path for Java developers. Some might like the expressiveness as well over Go.
Both ecosystems complement themselves, some stuff Java ecosystem does much better than .NET, like multiple implementations, with various kinds of GC and JIT implementations, wider support of hardware deployments, tooling like Graal, industry standards, IDE implementations, a mobile OS,...
Other things the .NET ecosystem does better, support for value types, low level programming, SIMD, desktop development, game engines.
To this day they keep copying features from each other, and if either C# or Java isn't one's taste, there are still several other options on each platform.
Hence why I am confortably at home using them both, complemented by JS/TS for FE, and C++ for fiddling with their runtimes, or plugging into native libraries.
More recently, default interface methods, apparently the ongoing discriminate union design seems to now be settling on similar Scala/Java approach, I still have to spend some time delving into it.
Not delving now on the CLR, versus the various JVM implementations, regarding GC, JIT, PGO, tiered compilation, escape analysis,...
Indeed!
But to contradict myself, I actually use windows, WSL and oracle cloud to build for windows x86, linux x86 and linux ARM64 to build a java desktop application. Of course I'd prefer it if I could just use my main machine and OS to build all those like Go can.
(It really does loadClass everything reachable based on the code to be compiled)
wasting gigabytes of ram and fast startup times
>and identify the optimizations on the fly
and still get gapped by statically compiled languages.
I guess these are a cheap price to pay for the benefit of ... having to install and patch runtimes.
Peak-peak performance is not as good as JVM in many cases, but how long does your process actually spend within that space? Native Image can be optimal for many cases
Which statically complicated languages are you referring to? :)
--
You are thinking of AOT compiled, but does that AOT language support arbitrary class loading to expand functionality, live observability while taking almost zero overhead for that?
--
So what do you do if a statically linked library of yours has a vulnerability?
> this is a huge downside.
The compiler can only do static analysis and code generation. I'd much rather a hotspot VM analyze the running code and identify the optimizations on the fly.
So I believe we're in agreement, that AOT imposes does limited optimizations compared to HotSpot JIT
Plz upvote
Anyone know if this true?
https://blogs.oracle.com/java/post/graalvm-free-license
So, yes, GraalVM CE is slightly nerfed (the performance difference across all EE features is like 30% maximum, at peak, in limited circumstances). You also do not need to use GraalVM CE in as many places anymore, and Oracle GraalVM with G1 is a free download. Many free projects like Pkl use the full-blown VM.
https://www.graalvm.org/latest/reference-manual/native-image...
Serial GC is still very performant. It's great for things like command line tools which don't need a low-pause long-running GC like G1.
Also it isn't that easy if any of GUI frameworks from Microsoft are being used.
Ah, and if using WinUI, one even has the additional requirement that only UI elements implemented with partial classes can be AOT compiled due to WinUI magic code.
I do agree Avalonia and Uno are great projects.
There needs to be more context here. I think the BCL is fully annotated already. Many of MS libraries are too including gRPC.
Your gRPC example is also quite interesting, I am yet to use it in anger, and never seen it being used at any of our .NET customers.
Meanwhile as mentioned, GUI frameworks aren't supported, WinUI requires partial classes and only kind of works, ASP.NET only if using minimal APIs, EF partially, and so on.
So it isn't really telling the whole story that "dotnet publish /p:PublishAot=true" works regardless of the .NET codebase being compiled.
Portability is by definition not possible with natively compiled binaries however. It is a choice - either produce an assembly (still single-file, .NET can do this unlike JVM offerings) that is completely portable, or produce a self-contained executable targeted at a particular OS and ISA.
Sure, but not cross-platform, which the previous comment seemed to indicate that GraalVM could, which it cannot. Both tools need to compile on the platform they target, compared to Go/Rust where you can target other platforms even though you're not compiling on them.
As for GraalVM, it has plenty of modes, the AOT one that is being talked about here, is native image.
Which can also use a JAR file as input, so even though it doesn't yet support cross-compilation, you can use the JAR file as single source, and then have a couple of build environments in e.g. Github pipelines.
https://blogs.oracle.com/developers/post/building-cross-plat...
Does it take a couple of more steps? Yes it does.
To the end user it doesn't matter as long as they get their shinny executable available, and it only has to be done once as a project template.
Also note that cross-compiling only really works without issues for whatever compiled language, if the toolchains have access to every single library from the target system on the host environment, and no dependencies FFI into other languages.
You are also confusing what it means to produce a portable assembly with the ability to target across operating systems or CPU architectures.
(also, most native toolchains do not let you easily cross-compile, Go makes significant sacrifices to make it possible, as long as you have no dependencies which rely on C/C++, and there are many of those)