Java 16 EA Alpine & JLink vs Graal
Tags: java, linux, docker, performance
Published: 2021-01-17 19:24:29

My first post... hurrah!

As the saying goes: good things come in small packages. For container images it means a smaller attack surface and faster to deploy and start-up. Unfortunately, Java hasn't been the best candidate for containerisation but that has changed with a few interesting developments:
* [JEP 386: Alpine Linux Port](http://openjdk.java.net/jeps/386): There have been other Alpine builds of various quality, but now it's official,
* [JEP 282: jlink: The Java Linker](https://openjdk.java.net/jeps/282): Leveraging the Module System to create builds with just the bits you use,
* [Graal Native Image](https://www.graalvm.org/reference-manual/native-image/): A statically linked Java image.

With Java 16 we can now use JLink with Alpine images to get all the next gen GCs (a-la ZGC and Shenandoah) goodness.

I thought it would be useful to compare and contrast them with a simple ReST service. For this I'm using `io.helidon.archetypes:helidon-quickstart-mp:2.2.0` archetype as a base with some customisations to the `Dockerfile`s.

> :information_source: This is a Microprofile implementation so that means full-fat CDI, JAX-RS goodness which IMHO gives a good compromise between standards, developer productivity and scalability.

There were some changes required to the files.

For `Dockerfile`:
```diff
-FROM openjdk:11-jre-slim
+FROM openjdk:16-jdk-alpine
```

For `Dockerfile.jlink`:
```diff
-FROM maven:3.6.3-jdk-11-slim as build
+FROM openjdk:16-jdk-alpine as build
+RUN apk add --no-cache bash maven

-FROM debian:stretch-slim
+FROM alpine:3.13.0
+RUN apk add --no-cache bash
```

> :information_source: We must add `bash` to the Alpine JLink image which adds 2MB as Helidon is using a little script to start up. Ideally if productionising this I would aim to remove this dependency.

So, what do the images sizes look like using the Slim JDK image as a control:

| Image | Size | Change |
| --- | --- | --- | 
| JDK 11 Slim (Debian) | 220MB | 100% |
| Alpine | 338MB | 154% |
| Alpine JLink | 127MB | 58% |
| Graal native image | 94MB | 43% |

As expected Graal based on `scratch` image is very small followed by Alpine JLink, Slim and finally Alpine at the rear. Now for start up time:

| Image | Time | Change |
| --- | --- | --- |
| JDK 11 Slim (Debian) | 4,493ms | 100% |
| Alpine | 3,310ms | 74% |
| Alpine JLink | 1,844ms | 41% |
| Graal native image | 80ms | 2% |

Again, not many surprises here except Graal is faster than I thought!

# Thoughts #

It's a very interesting time for Java and containers. Java can be lean, light and [fast](https://medium.com/helidon/can-java-microservices-be-as-fast-as-go-5ceb9a45d673) which still makes it relevant in a container native deployment.

Both JLink ([gRPC#3522](https://github.com/grpc/grpc-java/issues/3522)) and Graal have some issues; I'm especially concerned about the Serial GC in Graal so will be putting that under some stress soon to see if that confirms my suspicions. I'll also be good when some Java 16 JRE Alpine images appear as the JDK is too bloaty.

Jury is out if Graal or JLink is my preferred approach until I can stick load on them and see how they behave in Kubernetes but things are looking good.