*Really* Small Java Apps :: Mar 4, 2019
Since bundling apps and runtime is the new best-practice (whether with Docker or jpackage), and the full JVM weighs in at hundreds of megabytes, how can we include only the minimal JVM subset our application requires? This post explores 4 complementary ways:
- Build a minimal JDK image
- Use jlink to create an application image
- Choose a minimal docker image
- Use the jlink image with jpackage
Building the JDK
OpenJDK has many features, including 7 Garbage Collectors (Epsilon, Serial, Parallel, CMS, G1, Shennandoah, ZGC!!) and experimental compilers like Graal. Building the OpenJDK is suprisingly easy (took 12 minutes for a fresh build on my 2015 macbook), so lets strip out what we don’t need!
We care about two configure
options: --with-jvm-variants=<variant>[,<variant>...]
and --with-jvm-features=<feature>[,<feature>...]
. By default configure
uses the server
variant, which includes features:
aot
: experimental ahead of time compilationcds
: class Data Sharingcmsgc
: concurrent mark sweep gccompiler1
: the C1 compilercompiler2
: the C2 compilerdtrace
: support for the tracing frameworkepsilongc
: a no-op gcg1gc
: the garbage-first garbage collectorgraal
: experimental C2 replacement written in Javajfr
: Java Flight Recorder supportjni-check
: Java Native Interface supportjvmci
: the compiler interface needed for Graal and other plugin-in JITsjvmti
: tools interface for debuggers and profilersmanagement
: app monitoring/management support with Java Management Extensionsnmt
: Native Memory Trackingparallelgc
: the Parallel GCserialgc
: the Serial GCservices
: not really sure.. maybe the ServiceProvider api?shenandoahgc
: Shenandoah GCvm-structs
: for servicability agents
The other varients are server, client, minimal, core, zero, and custom. minimal
comes with compiler1, dtrace, and serialgc.
My app doesn’t need most of this, so I build a minimal JDK with C1, C2, DTrace, G1GC, and CDS (for quicker startup, requires G1GC):
Configure should spit out something like
Then make:
You can now use java
, jlink
, etc from the build output.
jlink
If your app uses the Java Module System, it’s easy to make a minimal JRE image, with just the modules you require:
Make sure not to use compress=2, since your applicationn will be slower, AND larger (at least for now).
Docker
Google’s Distroless project has minimal docker images. If using a jlink image then the base image (8mb) should be prefered.
Jpackage
There are early-access builds of jpackage available. Jlinked images can be specified with an option.
Conclusions
For a simple project requiring java.net.http, using these steps produced a 23MB jlink image. Hopefully this can be improved further if jlink improves –compress=2 to use LZ4 (https://twitter.com/shipilev/status/1100396679285665794)