Hacker News new | ask | show | jobs
by jagged-chisel 453 days ago
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.

3 comments

> 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.

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.

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.
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).