*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=
- `aot`: experimental ahead of time compilation
- `cds`: class Data Sharing
- `cmsgc`: concurrent mark sweep gc
- `compiler1`: the C1 compiler
- `compiler2`: the C2 compiler
- `dtrace`: support for the tracing framework
- `epsilongc`: a no-op gc
- `g1gc`: the garbage-first garbage collector
- `graal`: experimental C2 replacement written in Java
- `jfr`: Java Flight Recorder support
- `jni-check`: Java Native Interface support
- `jvmci`: the compiler interface needed for Graal and other plugin-in JITs
- `jvmti`: tools interface for debuggers and profilers
- `management`: app monitoring/management support with Java Management Extensions
- `nmt`: Native Memory Tracking
- `parallelgc`: the Parallel GC
- `serialgc`: the Serial GC
- `services`: not really sure.. maybe the ServiceProvider api?
- `shenandoahgc`: Shenandoah GC
- `vm-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):
bash configure --with-jvm-variants=minimal \
--with-jvm-features=compiler2,g1gc,cds
Configure should spit out something like
====================================================
The existing configuration has been successfully updated in
/Users/august/Documents/prog/openjdk/jdk/build/macosx-x86_64-minimal-release
using configure arguments '--with-jvm-variants=minimal --with-jvm-features=compiler2,g1gc,cds'.
Configuration summary:
* Debug level: release
* HS debug level: product
* JVM variants: minimal
* JVM features: minimal: 'cds compiler1 compiler2 dtrace g1gc minimal serialgc'
* OpenJDK target: OS: macosx, CPU architecture: x86, address length: 64
* Version string: 13-internal+0-adhoc.august.jdk (13-internal)
Tools summary:
* Boot JDK: openjdk version "13-ea" 2019-09-17 OpenJDK Runtime Environment (build 13-ea+6) OpenJDK 64-Bit Server VM (build 13-ea+6, mixed mode, sharing) (at /Library/Java/JavaVirtualMachines/jdk-13.jdk/Contents/Home)
* Toolchain: clang (clang/LLVM from Xcode 10.1)
* C Compiler: Version 10.0.0 (at /usr/bin/clang)
* C++ Compiler: Version 10.0.0 (at /usr/bin/clang++)
Build performance summary:
* Cores to use: 4
* Memory limit: 8192 MB
Then make:
make images CONF=macosx-x86_64-minimal-release
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:
jlink --compress=1 --no-header-files \
--no-man-pages --strip-debug \
--output --module-path \
--add-modules [,...]
Make sure not to use compress=2, since your applicationn will be slower, AND larger (at least for now).