We initially used GraalVM Native Image as a way to deploy JavaFX to mobile targets. However, after seeing our mobile apps feel snappier than our desktop apps, we gradually migrated all of our GUI and CLI applications over to ahead-of-time (AOT) compilation. GUIs mostly run in interpreted mode (users would have to click thousands of times to reach the JIT threshold), and with AOT we see up to 90% reductions in startup and first visit times, which results in a noticeably better user experience.
| AtlantaFX Sampler (RPi5) | jlink (JIT) | jlink + CDS (JIT) | Native Image (AOT) |
|---|---|---|---|
Distribution size | 139 MB | 155 MB (+12%) | 124 MB (-11%) |
Time to first window | 3.6 s | 2.7 s (-26%) | 0.5 s (-86%) |
First visit: HTMLEditor (WebView) | 2.39 s | 2.31 s (-3%) | 0.22 s (-91%) |
First visit: Overview (FXML) | 1.90 s | 1.50 s (-21%) | 0.32 s (-83%) |
Private memory after startup | 224 MB | 244 MB (+9%) | 164 MB (-27%) |
Private memory after all pages | 1424 MB | 1396 MB (-2%) | 634 MB (-55%) |
In fact, starting the AtlantaFX sampler on a high-end Ryzen 9 9950X desktop with jlink + CDS takes more than 2.5 times as long (1.3 s) as the same application running AOT-compiled on a tiny Raspberry Pi 5 (0.5 s, see benchmarks). The whole run is captured in a short video walkthrough of all pages, including FXML, WebView, MediaPlayer, and AWT integration.
JavaFX actually works very well in Native Image once everything is configured, but getting a complex application running for the first time is typically not a straightforward experience. This steep initial hurdle has unfortunately earned JavaFX, and desktop Java in general, a reputation of being incompatible or working poorly with Native Image. I think a big part of this stems from complex, specialized tooling that performs a lot of "magic" behind the scenes to hide the native layers. Whenever anything breaks, it produces errors that many Java developers don’t know how to debug.
In this post I want to demystify the internals of these tools, and show why we built our own tooling to run the latest JavaFX 27 release with the latest enterprise Oracle GraalVM on desktop targets.