Hacker News new | ask | show | jobs
by islon 1252 days ago
No. You are thinking of GraalVM native image which will compile java to native machine code.

jlink is more similar to tree shaking: it strips the JRE of anything your program don't need.

2 comments

But I think the parent's point is given reflection, how can jlink statically know the complete set of classes that your program needs?
It's only tree shaking the JRE itself, not your whole program (unfortunately..). So as I understand it, it means no dynamically calling arbitrary classes in the JRE, but that's a much narrower limitation.

Final binary size is naturally vastly reduced b/c you won't have the whole JRE, but last I tested you still end up with very chunky executables (minimal JFX GUIs were coming out to 100-200 MB)

100-200mb is a bit too large for a minimal JFX app, though it may depend on how you define minimal:

    conveyor generate javafx com.example.jfx-test && cd jfx-test
    ./gradlew jar
    conveyor make windows-app
    du -h output
71mb on disk. 31mb package size. That's a bit bigger than strictly necessary. It includes FXML and see the discussion of optional plugin modules elsewhere.

However you can easily get to 150-200mb on disk by using javafx.web because that includes a custom build of WebKit which is ~75mb all by itself.

Yeah, removing javafx-media and javafx-web saves a ton. It's good to point that out. It's been a couple of years, so I'll need to revisit this again later and try again

> It includes FXML and see the discussion of optional plugin modules elsewhere.

Where is this?

And do you know if it's still not possible to generate an .exe? I remember that while I was turned off by the final files sizes, what really made me drop jlink was that the final built target would always be some baroque installer (which makes sense for large GUIs that need to maintain state between runs). But you couldn't just generate a double-clickeable .exe or .appimage file that'd be equivalent to running your uberjar. So I stuck to the uberjar.. (now user have problem installing weird Java 11 Runtimes from not-Oracle)

(Further confusing things was that there is some intermediary build target in jlink also called `appimage` that's not actually an appimage, but it's quite similar..)

Elsewhere in this thread (look at my other comments).

jlink itself just outputs a directory tree. If you want the user to be able to double click an exe then there are several options but two are:

1. Use Gluon Native Image. This will AOT compile your Java code and statically link JavaFX to give you a genuine single EXE program, with no JVM, no JIT compilation, and fast startup. However your app needs to be native-image compatible and I don't know if every app turns into a single EXE.

2. Use (surprise) Conveyor, which will make an EXE that when opened downloads, installs and then immediately runs your app using a bundled JVM. If your app is already installed then it'll do an update check and then open it. And of course your app can be then invoked from the start menu.

The latter isn't genuinely a single EXE of course but the UX is similar and it handles the common case of needing to update either your app, or your JVM, or both.

Is there something that can do the tree-shaking of the Java program?
There's proguard, originally intended for obfuscation but also serves well for dumping unused code from your project and most importantly from third party libraries. Virtually every Android app has it in its build pipeline, or these days actually a proguard reimplementation by Google (R8) that reads proguard's rather byzantine but effective configuration syntax (you usually need to keep some stuff in libraries that talk to themselves only via reflection)
I think if you do Graal native compilation then you'd effectively get that. The linker should chuck unused code. (though tbh I haven't tried it myself)

But to just say "no reflection" and tree shake your JVM code - not that I'm aware of unfortunately! I'd love to just treeshake entire unused dependencies. At the moment I do it manually - but it's a chore and it's hard to do comprehensively.

Now that post- Java8 you're supposed to jlink the JRE, I somehow doubt this will ever happen. The people that care about executable size would probably be doing Graal Native.

I've used proguard to remove unused classes from dependencies.
Proguard is usually the tool to do that (commonly used on Android with its newest incarnation named R8).

You do need to manually annotate classes used by reflection so it doesn't remove (or obfuscate) them.

If you have a module-info file, you have all the used modules listed there and this jlink only has to look at that. If you use jdeps then it indeed can in itself find only the statically known modules.
GraalVM can use reflection just fine, with the caveat that you have to specify which classes can potentially be targeted (so no dynamically loading a random class file and reflection on that is possible)