Close

Java 9 - Creating Custom runtime Image with jlink

[Last Updated: Oct 12, 2018]

Java 9 introduced jlink command-line tool which assembles and optimizes the specified modules and their dependencies into a custom runtime image.

In other words it assembles a Java application and its dependent modules (instead of all modules which come with default JDK) into a custom JRE. The resultant JRE can execute the provided application without the need of a preinstalled JRE.

jlink also does a lot of optimizations that are otherwise difficult at compile time or costly at run-time.

Example

Let's create a very simple modular application. We are using JDK 11 on Windows 10.

jlink-example/src/com/logicbig/example/Test.java

package com.logicbig.example;

import java.util.logging.Logger;

public class Test {
  private static final Logger LOGGER = Logger.getLogger(Test.class.getName());

  public static void main(String[] args) {
      LOGGER.info("Running test application..");
  }
}

jlink-example/src/module-info.java

module test.example {
  requires java.logging;
}

Compiling and running

D:\jlink-example>tree /a /f
Folder PATH listing for volume Data
Volume serial number is 68F9-EDFA
D:.
\---src
| module-info.java
|
\---com
\---logicbig
\---example
Test.java

D:\jlink-example>javac -d out src\module-info.java
D:\jlink-example>javac -d out --module-path out src\com\logicbig\example\Test.java

After compilation:

D:\jlink-example>tree /a /f
Folder PATH listing for volume Data
Volume serial number is 68F9-EDFA
D:.
+---out
| | module-info.class
| |
| \---com
| \---logicbig
| \---example
| Test.class
|
\---src
| module-info.java
|
\---com
\---logicbig
\---example
Test.java

Let's run to see whether our application is working as expected:

D:\jlink-example>java --module-path out --module test.example/com.logicbig.example.Test
Oct 11, 2018 6:15:16 PM com.logicbig.example.Test main
INFO: Running test application..

The dependent modules

Let's use jdeps command to see the dependent modules:

D:\jlink-example>jdeps --module-path out -s --module test.example
test.example -> java.base
test.example -> java.logging

Creating runtime image with jlink

General jlink command:

jlink --module-path <module-path> --add-modules <comma-separated-module-names> --output <target-directory>

We just need to include our main modules for <comma-separated-module-names>, all other modules the main modules depend on will automatically be included.

Let's continue with our example application and create runtime image for it:

D:\jlink-example>jlink --module-path D:\java\jdk-11\jmods;out --add-modules test.example --output build

In above command we also included D:\java\jdk-11\jmods for --module-path, so that the required JDK modules (in our example: java.base and java.logging) can be resolved and included in the output image.

The generated image in 'build' folder:

D:\jlink-example\build>tree /a /f
Folder PATH listing for volume Data
Volume serial number is 68F9-EDFA
D:.
| release
|
+---bin
| | api-ms-win-core-console-l1-1-0.dll
| | api-ms-win-core-datetime-l1-1-0.dll
| | api-ms-win-core-debug-l1-1-0.dll
| | api-ms-win-core-errorhandling-l1-1-0.dll
| | api-ms-win-core-file-l1-1-0.dll
| | api-ms-win-core-file-l1-2-0.dll
| | api-ms-win-core-file-l2-1-0.dll
| | api-ms-win-core-handle-l1-1-0.dll
| | api-ms-win-core-heap-l1-1-0.dll
| | api-ms-win-core-interlocked-l1-1-0.dll
| | api-ms-win-core-libraryloader-l1-1-0.dll
| | api-ms-win-core-localization-l1-2-0.dll
| | api-ms-win-core-memory-l1-1-0.dll
| | api-ms-win-core-namedpipe-l1-1-0.dll
| | api-ms-win-core-processenvironment-l1-1-0.dll
| | api-ms-win-core-processthreads-l1-1-0.dll
| | api-ms-win-core-processthreads-l1-1-1.dll
| | api-ms-win-core-profile-l1-1-0.dll
| | api-ms-win-core-rtlsupport-l1-1-0.dll
| | api-ms-win-core-string-l1-1-0.dll
| | api-ms-win-core-synch-l1-1-0.dll
| | api-ms-win-core-synch-l1-2-0.dll
| | api-ms-win-core-sysinfo-l1-1-0.dll
| | api-ms-win-core-timezone-l1-1-0.dll
| | api-ms-win-core-util-l1-1-0.dll
| | api-ms-win-crt-conio-l1-1-0.dll
| | api-ms-win-crt-convert-l1-1-0.dll
| | api-ms-win-crt-environment-l1-1-0.dll
| | api-ms-win-crt-filesystem-l1-1-0.dll
| | api-ms-win-crt-heap-l1-1-0.dll
| | api-ms-win-crt-locale-l1-1-0.dll
| | api-ms-win-crt-math-l1-1-0.dll
| | api-ms-win-crt-multibyte-l1-1-0.dll
| | api-ms-win-crt-private-l1-1-0.dll
| | api-ms-win-crt-process-l1-1-0.dll
| | api-ms-win-crt-runtime-l1-1-0.dll
| | api-ms-win-crt-stdio-l1-1-0.dll
| | api-ms-win-crt-string-l1-1-0.dll
| | api-ms-win-crt-time-l1-1-0.dll
| | api-ms-win-crt-utility-l1-1-0.dll
| | java.dll
| | java.exe
| | javaw.exe
| | jimage.dll
| | jli.dll
| | keytool.exe
| | msvcp140.dll
| | net.dll
| | nio.dll
| | ucrtbase.dll
| | vcruntime140.dll
| | verify.dll
| | zip.dll
| |
| \---server
| jvm.dll
|
+---conf
| | logging.properties
| | net.properties
| |
| \---security
| | java.policy
| | java.security
| |
| \---policy
| | README.txt
| |
| +---limited
| | default_local.policy
| | default_US_export.policy
| | exempt_local.policy
| |
| \---unlimited
| default_local.policy
| default_US_export.policy
|
+---include
| | classfile_constants.h
| | jni.h
| | jvmti.h
| | jvmticmlr.h
| |
| \---win32
| jni_md.h
|
+---legal
| +---java.base
| | aes.md
| | asm.md
| | c-libutl.md
| | cldr.md
| | COPYRIGHT
| | icu.md
| | LICENSE
| | public_suffix.md
| | unicode.md
| | zlib.md
| |
| \---java.logging
| COPYRIGHT
| LICENSE
|
\---lib
| classlist
| jrt-fs.jar
| jvm.cfg
| jvm.lib
| modules
| tzdb.dat
| tzmappings
|
+---security
| blacklisted.certs
| cacerts
| default.policy
| public_suffix_list.dat
|
\---server
Xusage.txt

The size of the image (the 'build' folder) is about 40MB as compared to 277MB of the whole JDK 11.

Running application with generated image

We can run our main class from bin folder as follows:

D:\jlink-example\build\bin>java.exe --module test.example/com.logicbig.example.Test
Oct 11, 2018 6:15:31 PM com.logicbig.example.Test main
INFO: Running test application..

In above command 'java.exe' is the one which is included in bin folder, not the one on the classpath.

Using launcher option

--launcher command=moduleName/mainClass option of jlink can be used to create a 'launcher script' to run our application:

Let's use this option for our above example:

D:\jlink-example>rmdir /S /Q build
D:\jlink-example>jlink --launcher test-app=test.example/com.logicbig.example.Test  --module-path D:\java\jdk-11\jmods;out --add-modules test.example --output build

It created two launcher scripts test-app (for linux) and test-app.bat (for windows).

The content of test-app.bat

@echo off
set JLINK_VM_OPTIONS=
set DIR=%~dp0
"%DIR%\java" %JLINK_VM_OPTIONS% -m test.example/com.logicbig.example.Test %*

Let's run the script:

D:\jlink-example\build\bin>test-app.bat
Oct 11, 2018 6:15:41 PM com.logicbig.example.Test main
INFO: Running test application..

Example Project

Dependencies and Technologies Used:

  • JDK 9.0.1
Java 9 - jlink example Select All Download
  • jlink-example
    • src
      • com
        • logicbig
          • example
            • Test.java

    See Also