Run springboot2 using openjdk9-alpine

  java9

Order

This article mainly studies how to run springboot2 on docker’s java9 image and streamline jdk.

maven

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RC2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>9</java.version>
        <start-class>com.example.demo.DemoApplication</start-class>
    </properties>

Note that only version 2 of springboot can support java9, and this java.version is set to 9.
The version maven uses here is 3.3.3

mvn package

mvn clean package -Dmaven.test.skip=true
//......
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by lombok.javac.apt.LombokProcessor to field com.sun.tools.javac.processing.JavacProcessingEnvironment.discoveredProcs
WARNING: Please consider reporting this to the maintainers of lombok.javac.apt.LombokProcessor
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

You can see that the compilation can be successful here, but there is WARNING.

Docker build

Dockerfile

FROM dekstroza/openjdk9-alpine as packager

# First stage: JDK 9 with modules required for Spring Boot
RUN /opt/jdk-9/bin/jlink \
    --module-path /opt/jdk-9/jmods \
    --verbose \
    --add-modules java.base,java.logging,java.xml,jdk.unsupported,java.sql,java.desktop,java.management,java.naming,java.instrument \
    --compress 2 \
    --no-header-files \
    --output /opt/jdk-9-minimal

# Second stage, add only our custom jdk9 distro and our app
FROM alpine:3.6
COPY --from=packager /opt/jdk-9-minimal /opt/jdk-9-minimal
ENV JAVA_HOME=/opt/jdk-9-minimal
ENV PATH="$PATH:$JAVA_HOME/bin"

## copy app.jar
VOLUME /tmp
ADD app.jar /app.jar
#RUN sh -c 'touch /app.jar'

EXPOSE 8080
ENTRYPOINT java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=${PROFILE} -jar /app.jar

Alpine image is used here, because it is relatively small, the basic image is only a few meters, much smaller than ubuntu etc. Since alpine Linux uses MUSL as the standard C library, while openjdk relies on GNU Standard C Library (gclib), jdk9 of alpine version is required to run on alpine. However, there is no official Alpine image in jdk 9 at present, and there is only one version of early access.Announcing: Early-Access builds of JDK 9 for Alpine Linux/musl

Here we use dekstroza’s mirror image based on alpine3.6, dekstroza/openjdk9-alpine, as the basic mirror image of jlink, and then determine jmods (How will the following be determined?), and then use jlink to build the smallest jdk running environment.

Here’s –module-path (-p) Specifies the dependent module path; –add-modules add rootmodules for dependency analysis; –compress specifies the compression method, 0 is uncompressed, 1 is constant string sharing, and 2 is zip compression; -no-header-files specifies exclusion header files; –verbose is used to turn on trace output.

Build and run

mvn clean package -Dmaven.test.skip=true
cd target
cp ../src/main/docker/Dockerfile .
docker build -t springboot2-java9-demo .
docker run --rm -p 8080:8080 \
--name springboot2-java9-demo \
-e PROFILE=default \
springboot2-java9-demo

Determine which jmod to rely on

At present, no ready-made command/tool has been found to dynamically find the jmod on which the project depends. However, it can be implemented by building shell scripts through jdeps commands.

copy-dependencies

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Here, the dependent jar is stored in the classes/lib directory through the copy-dependencies plug-in, and then jdeps is used to specify this class-path to analyze app.jar (The main purpose of doing this is that springboot packages fatjar, and the relevant JARs are all in fatjar, so it is not good to specify class-path.)

jdeps recursive summary

➜  target git:(master) ✗ jdeps --class-path 'classes/lib/*' -recursive -summary app.jar
拆分程序包: javax.annotation [jrt:/java.xml.ws.annotation, classes/lib/javax.annotation-api-1.3.1.jar, classes/lib/jsr305-1.3.9.jar]

app.jar -> classes/lib/commons-lang3-3.7.jar
app.jar -> classes/lib/guava-24.0-jre.jar
app.jar -> java.base
app.jar -> java.logging
app.jar -> classes/lib/reactive-streams-1.0.2.jar
app.jar -> classes/lib/reactor-core-3.1.4.RELEASE.jar
app.jar -> classes/lib/rest-common-0.0.2.jar
app.jar -> classes/lib/slf4j-api-1.7.25.jar
app.jar -> classes/lib/spring-beans-5.0.4.RELEASE.jar
app.jar -> classes/lib/spring-boot-2.0.0.RC2.jar
app.jar -> classes/lib/spring-boot-autoconfigure-2.0.0.RC2.jar
app.jar -> classes/lib/spring-context-5.0.4.RELEASE.jar
app.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
app.jar -> classes/lib/spring-web-5.0.4.RELEASE.jar
app.jar -> classes/lib/spring-webflux-5.0.4.RELEASE.jar
checker-compat-qual-2.0.0.jar -> java.base
commons-lang3-3.7.jar -> java.base
error_prone_annotations-2.1.3.jar -> java.base
guava-24.0-jre.jar -> classes/lib/checker-compat-qual-2.0.0.jar
guava-24.0-jre.jar -> classes/lib/error_prone_annotations-2.1.3.jar
guava-24.0-jre.jar -> java.base
guava-24.0-jre.jar -> java.logging
logback-classic-1.2.3.jar -> java.base
logback-classic-1.2.3.jar -> java.management
logback-classic-1.2.3.jar -> java.naming
logback-classic-1.2.3.jar -> java.xml
logback-classic-1.2.3.jar -> jdk.unsupported
logback-classic-1.2.3.jar -> classes/lib/logback-core-1.2.3.jar
logback-classic-1.2.3.jar -> classes/lib/slf4j-api-1.7.25.jar
logback-classic-1.2.3.jar -> 找不到
logback-core-1.2.3.jar -> java.base
logback-core-1.2.3.jar -> java.xml
logback-core-1.2.3.jar -> 找不到
netty-buffer-4.1.21.Final.jar -> java.base
netty-buffer-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-codec-4.1.21.Final.jar -> java.base
netty-codec-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-codec-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-codec-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
netty-codec-4.1.21.Final.jar -> 找不到
netty-codec-http-4.1.21.Final.jar -> java.base
netty-codec-http-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-codec-http-4.1.21.Final.jar -> classes/lib/netty-codec-4.1.21.Final.jar
netty-codec-http-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-codec-http-4.1.21.Final.jar -> classes/lib/netty-handler-4.1.21.Final.jar
netty-codec-http-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
netty-codec-socks-4.1.21.Final.jar -> java.base
netty-codec-socks-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-codec-socks-4.1.21.Final.jar -> classes/lib/netty-codec-4.1.21.Final.jar
netty-codec-socks-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-codec-socks-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
netty-common-4.1.21.Final.jar -> java.base
netty-common-4.1.21.Final.jar -> java.logging
netty-common-4.1.21.Final.jar -> jdk.unsupported
netty-common-4.1.21.Final.jar -> classes/lib/slf4j-api-1.7.25.jar
netty-common-4.1.21.Final.jar -> 找不到
netty-handler-4.1.21.Final.jar -> java.base
netty-handler-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-handler-4.1.21.Final.jar -> classes/lib/netty-codec-4.1.21.Final.jar
netty-handler-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-handler-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
netty-handler-4.1.21.Final.jar -> 找不到
netty-handler-proxy-4.1.21.Final.jar -> java.base
netty-handler-proxy-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-handler-proxy-4.1.21.Final.jar -> classes/lib/netty-codec-4.1.21.Final.jar
netty-handler-proxy-4.1.21.Final.jar -> classes/lib/netty-codec-http-4.1.21.Final.jar
netty-handler-proxy-4.1.21.Final.jar -> classes/lib/netty-codec-socks-4.1.21.Final.jar
netty-handler-proxy-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-handler-proxy-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
netty-resolver-4.1.21.Final.jar -> java.base
netty-resolver-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-transport-4.1.21.Final.jar -> java.base
netty-transport-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-transport-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-transport-4.1.21.Final.jar -> classes/lib/netty-resolver-4.1.21.Final.jar
netty-transport-native-epoll-4.1.21.Final.jar -> java.base
netty-transport-native-epoll-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-transport-native-epoll-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-transport-native-epoll-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
netty-transport-native-epoll-4.1.21.Final.jar -> classes/lib/netty-transport-native-unix-common-4.1.21.Final.jar
netty-transport-native-unix-common-4.1.21.Final.jar -> java.base
netty-transport-native-unix-common-4.1.21.Final.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
netty-transport-native-unix-common-4.1.21.Final.jar -> classes/lib/netty-common-4.1.21.Final.jar
netty-transport-native-unix-common-4.1.21.Final.jar -> classes/lib/netty-transport-4.1.21.Final.jar
reactive-streams-1.0.2.jar -> java.base
reactor-core-3.1.4.RELEASE.jar -> java.base
reactor-core-3.1.4.RELEASE.jar -> java.logging
reactor-core-3.1.4.RELEASE.jar -> classes/lib/reactive-streams-1.0.2.jar
reactor-core-3.1.4.RELEASE.jar -> classes/lib/slf4j-api-1.7.25.jar
reactor-core-3.1.4.RELEASE.jar -> 找不到
reactor-netty-0.7.4.RELEASE.jar -> java.base
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-codec-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-codec-http-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-common-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-handler-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-handler-proxy-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-resolver-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-transport-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/netty-transport-native-epoll-4.1.21.Final.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/reactive-streams-1.0.2.jar
reactor-netty-0.7.4.RELEASE.jar -> classes/lib/reactor-core-3.1.4.RELEASE.jar
reactor-netty-0.7.4.RELEASE.jar -> 找不到
rest-common-0.0.2.jar -> java.base
slf4j-api-1.7.25.jar -> java.base
slf4j-api-1.7.25.jar -> classes/lib/logback-classic-1.2.3.jar
spring-aop-5.0.4.RELEASE.jar -> java.base
spring-aop-5.0.4.RELEASE.jar -> classes/lib/spring-beans-5.0.4.RELEASE.jar
spring-aop-5.0.4.RELEASE.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-aop-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-beans-5.0.4.RELEASE.jar -> java.base
spring-beans-5.0.4.RELEASE.jar -> java.desktop
spring-beans-5.0.4.RELEASE.jar -> java.xml
spring-beans-5.0.4.RELEASE.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-beans-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-beans-5.0.4.RELEASE.jar -> 找不到
spring-boot-2.0.0.RC2.jar -> java.base
spring-boot-2.0.0.RC2.jar -> java.desktop
spring-boot-2.0.0.RC2.jar -> java.management
spring-boot-2.0.0.RC2.jar -> java.xml
spring-boot-2.0.0.RC2.jar -> classes/lib/spring-beans-5.0.4.RELEASE.jar
spring-boot-2.0.0.RC2.jar -> classes/lib/spring-context-5.0.4.RELEASE.jar
spring-boot-2.0.0.RC2.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-boot-2.0.0.RC2.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-boot-2.0.0.RC2.jar -> classes/lib/spring-web-5.0.4.RELEASE.jar
spring-boot-2.0.0.RC2.jar -> 找不到
spring-boot-autoconfigure-2.0.0.RC2.jar -> java.base
spring-boot-autoconfigure-2.0.0.RC2.jar -> classes/lib/spring-beans-5.0.4.RELEASE.jar
spring-boot-autoconfigure-2.0.0.RC2.jar -> classes/lib/spring-boot-2.0.0.RC2.jar
spring-boot-autoconfigure-2.0.0.RC2.jar -> classes/lib/spring-context-5.0.4.RELEASE.jar
spring-boot-autoconfigure-2.0.0.RC2.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-boot-autoconfigure-2.0.0.RC2.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar -> java.base
spring-context-5.0.4.RELEASE.jar -> java.desktop
spring-context-5.0.4.RELEASE.jar -> java.instrument
spring-context-5.0.4.RELEASE.jar -> java.management
spring-context-5.0.4.RELEASE.jar -> java.naming
spring-context-5.0.4.RELEASE.jar -> java.xml
spring-context-5.0.4.RELEASE.jar -> java.xml.ws
spring-context-5.0.4.RELEASE.jar -> java.xml.ws.annotation
spring-context-5.0.4.RELEASE.jar -> classes/lib/spring-aop-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar -> classes/lib/spring-beans-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar -> classes/lib/spring-expression-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-context-5.0.4.RELEASE.jar -> 找不到
spring-core-5.0.4.RELEASE.jar -> java.base
spring-core-5.0.4.RELEASE.jar -> java.desktop
spring-core-5.0.4.RELEASE.jar -> java.sql
spring-core-5.0.4.RELEASE.jar -> java.xml
spring-core-5.0.4.RELEASE.jar -> jdk.unsupported
spring-core-5.0.4.RELEASE.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
spring-core-5.0.4.RELEASE.jar -> classes/lib/netty-common-4.1.21.Final.jar
spring-core-5.0.4.RELEASE.jar -> classes/lib/reactive-streams-1.0.2.jar
spring-core-5.0.4.RELEASE.jar -> classes/lib/reactor-core-3.1.4.RELEASE.jar
spring-core-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-core-5.0.4.RELEASE.jar -> 找不到
spring-expression-5.0.4.RELEASE.jar -> java.base
spring-expression-5.0.4.RELEASE.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-expression-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-jcl-5.0.4.RELEASE.jar -> java.base
spring-jcl-5.0.4.RELEASE.jar -> java.logging
spring-jcl-5.0.4.RELEASE.jar -> classes/lib/slf4j-api-1.7.25.jar
spring-jcl-5.0.4.RELEASE.jar -> 找不到
spring-web-5.0.4.RELEASE.jar -> java.base
spring-web-5.0.4.RELEASE.jar -> classes/lib/netty-buffer-4.1.21.Final.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/netty-codec-http-4.1.21.Final.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/netty-transport-4.1.21.Final.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/reactive-streams-1.0.2.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/reactor-core-3.1.4.RELEASE.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/reactor-netty-0.7.4.RELEASE.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/spring-beans-5.0.4.RELEASE.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/spring-context-5.0.4.RELEASE.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-web-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-web-5.0.4.RELEASE.jar -> 找不到
spring-webflux-5.0.4.RELEASE.jar -> java.base
spring-webflux-5.0.4.RELEASE.jar -> classes/lib/reactive-streams-1.0.2.jar
spring-webflux-5.0.4.RELEASE.jar -> classes/lib/reactor-core-3.1.4.RELEASE.jar
spring-webflux-5.0.4.RELEASE.jar -> classes/lib/spring-context-5.0.4.RELEASE.jar
spring-webflux-5.0.4.RELEASE.jar -> classes/lib/spring-core-5.0.4.RELEASE.jar
spring-webflux-5.0.4.RELEASE.jar -> classes/lib/spring-jcl-5.0.4.RELEASE.jar
spring-webflux-5.0.4.RELEASE.jar -> classes/lib/spring-web-5.0.4.RELEASE.jar

Since CLASSES/LIB/LOG4J-API-2.10.0.JAR is multi-release, multi-release 9 needs to be specially specified, but since recursive is used, it is temporarily deleted and then analyzed here.
Find java,jdk’s beginning through output, and get the required jmods after deduplication.

java.base,java.logging,java.xml,jdk.unsupported,java.sql,java.desktop,java.management,java.naming,java.instrument

jdk-internals

➜  target git:(master) ✗ jdeps --class-path 'classes/lib/*' -recursive --jdk-internals  app.jar
拆分程序包: javax.annotation [jrt:/java.xml.ws.annotation, classes/lib/javax.annotation-api-1.3.1.jar, classes/lib/jsr305-1.3.9.jar]

fastjson-1.2.35.jar -> java.base
   com.alibaba.fastjson.serializer.AnnotationSerializer -> sun.reflect.annotation.AnnotationType              JDK internal API (java.base)
guava-24.0-jre.jar -> jdk.unsupported
   com.google.common.cache.Striped64                  -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.cache.Striped64$1                -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.cache.Striped64$Cell             -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.LittleEndianByteArray$UnsafeByteArray -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$3 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.Striped64                   -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.Striped64$1                 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.hash.Striped64$Cell              -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper$1 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
logback-classic-1.2.3.jar -> jdk.unsupported
   ch.qos.logback.classic.spi.PackagingDataCalculator -> sun.reflect.Reflection                             JDK internal API (jdk.unsupported)
lombok-1.16.20.jar -> jdk.compiler
   lombok.javac.apt.Processor                         -> com.sun.tools.javac.processing.JavacFiler          JDK internal API (jdk.compiler)
   lombok.javac.apt.Processor                         -> com.sun.tools.javac.processing.JavacProcessingEnvironment JDK internal API (jdk.compiler)
   lombok.javac.apt.Processor                         -> com.sun.tools.javac.util.Context                   JDK internal API (jdk.compiler)
   lombok.javac.apt.Processor                         -> com.sun.tools.javac.util.Options                   JDK internal API (jdk.compiler)
netty-common-4.1.21.Final.jar -> jdk.unsupported
   io.netty.util.internal.CleanerJava9                -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.PlatformDependent$Mpsc$1    -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.PlatformDependent0          -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.PlatformDependent0$1        -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.PlatformDependent0$2        -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.PlatformDependent0$3        -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueConsumerNodeRef -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueProducerNodeRef -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.LinkedQueueNode -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
netty-handler-4.1.21.Final.jar -> java.base
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.util.ObjectIdentifier                 JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.AlgorithmId                      JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateAlgorithmId           JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateIssuerName            JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateSerialNumber          JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateSubjectName           JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateValidity              JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateVersion               JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.CertificateX509Key               JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.X500Name                         JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.X509CertImpl                     JDK internal API (java.base)
   io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator -> sun.security.x509.X509CertInfo                     JDK internal API (java.base)
objenesis-2.6.jar -> jdk.unsupported
   org.objenesis.instantiator.sun.UnsafeFactoryInstantiator -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   org.objenesis.instantiator.util.ClassDefinitionUtils -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   org.objenesis.instantiator.util.UnsafeUtils        -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
reactor-core-3.1.4.RELEASE.jar -> jdk.unsupported
   reactor.core.publisher.MultiProducerRingBuffer     -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   reactor.core.publisher.RingBuffer                  -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   reactor.core.publisher.RingBufferFields            -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   reactor.core.publisher.UnsafeSequence              -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   reactor.core.publisher.UnsafeSupport               -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
spring-core-5.0.4.RELEASE.jar -> jdk.unsupported
   org.springframework.objenesis.instantiator.sun.UnsafeFactoryInstantiator -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   org.springframework.objenesis.instantiator.util.ClassDefinitionUtils -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
   org.springframework.objenesis.instantiator.util.UnsafeUtils -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)

警告: 不支持 JDK 内部 API, 它们专用于通过不兼容方式来
删除或更改的 JDK 实现, 可能会损坏您的应用程序。
请修改您的代码, 消除与任何 JDK 内部 API 的相关性。
有关 JDK 内部 API 替换的最新更新, 请查看:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool

JDK 内部 API                               建议的替换
----------                               -----
com.sun.tools.javac.processing.JavacFiler Use javax.tools and javax.lang.model @since 1.6
com.sun.tools.javac.processing.JavacProcessingEnvironment Use javax.tools and javax.lang.model @since 1.6
com.sun.tools.javac.util.Context         Use javax.tools and javax.lang.model @since 1.6
com.sun.tools.javac.util.Options         Use javax.tools and javax.lang.model @since 1.6
sun.misc.Unsafe                          See http://openjdk.java.net/jeps/260
sun.reflect.Reflection                   Use java.lang.StackWalker @since 9
sun.reflect.annotation.AnnotationType    Removed. See http://openjdk.java.net/jeps/260
sun.security.x509.X500Name               Use javax.security.auth.x500.X500Principal @since 1.4

It can be found that netty,netty, etc. have also used sun.misc.Unsafe, which is classified as jdk.unsupported. As its name implies, it does not recommend user program calls. It was originally intended for oracle jdk team to use.

Summary

For a jdk8 image, ubuntu may require 600 to 700 meters, while alpine requires about 200M meters. After passing jlink, java9 in this example is 63.22M meters in size, and springboot2 fatjar is 87.54M meters in total.

At present, there are still many deficiencies in the methods used in this article, mainly as follows:

  • At present, only the Early-Access build version of openjdk compiled with MUSL has not been officially released.
  • The jmod specified by dockerfile needs to be determined dynamically by script to parse the dependent jar package.
  • Springboot Project and many third-party class libraries have not yet used java9 module system. Although java9 supports jar packages before java9 throug h unnamed module, it is the best to migrate to java9 after all.
  • Maven-related plugin such as jlink,jmod JMOD are only pre-release, and have not been officially released yet.

The surrounding facilities of jdk9 still need to be further improved.

doc