1. Overview

jlink is a tool that generates a custom Java runtime image that contains only the platform modules that are required for a given application.

Such a runtime image acts exactly like the JRE but contains only the modules we picked and the dependencies they need to function. The concept of modular runtime images was introduced in JEP 220.

In this tutorial, we'll learn how to create a custom JRE using jlink, and we'll also run and test that our module functions correctly inside our JRE.

2. Need to Create a Custom JRE

Let's understand the motivation behind custom runtime images with an example.

We'll create a simple modular application. To know more about creating modular applications, please refer to our article on modularity.

First, let's create a HelloWorld class and a corresponding module:

public class HelloWorld {
    private static final Logger LOG = Logger.getLogger(HelloWorld.class.getName());
    public static void main(String[] args) {
        LOG.info("Hello World!");
    }
}
module jlinkModule {
    requires java.logging;
}

To run this program, we only need HelloWorld, String, Logger, and Object classes.

Even though this program needs only four classes to run, all the predefined classes in the JRE also get executed, even if our program doesn't require them.

Therefore, to run a small program, we have to maintain a complete JRE, which is simply a waste of memory.

So, a customized JRE is the best option to run our example.

With jlink, we can create our own, small JRE that contains only the relevant classes that we want to use, without wasting memory, and as a result, we'll see increased performance.

3. Building Custom Java Runtime Images

We'll perform a series of simple steps to create custom JRE images.

3.1. Compiling a Module

First, let's compile the program mentioned above from the command line:

javac -d out module-info.java
javac -d out --module-path out com\baeldung\jlink\HelloWorld.java

Now, let's run the program:

java --module-path out --module jlinkModule/com.baeldung.jlink.HelloWorld

The output will be:

Mar 13, 2019 10:15:40 AM com.baeldung.jlink.HelloWorld main
INFO: Hello World!

3.2. Using jdeps to List the Dependent Modules

In order to use jlink, we need to know the list of the JDK modules that the application uses and that we should include in our custom JRE.

Let's use the jdeps command to get the dependent modules used in the application:

jdeps --module-path out -s --module jlinkModule

The output will be:

jlinkModule -> java.base
jlinkModule -> java.logging

This makes sense, as java.base is the minimum module needed for Java code libraries, and java.logging is used by a logger in our program.

To create a custom JRE for a module-based application, we can use the jlink command. Here's its basic syntax:

jlink [options] –module-path modulepath
  –add-modules module [, module…]
  --output <target-directory>

Now, let's create a custom JRE for our program using Java 11:

jlink --module-path "%JAVA_HOME%\jmods";out
  --add-modules jlinkModule
  --output customjre

Here, the value after the –add-modules parameter tells jlink which module to include in the JRE.

Finally, the customjre next to the –output parameter defines the target directory where our custom JRE should be generated.

Note, we use Windows shell to execute all the commands throughout this tutorial. Linux and Mac users might have to slightly adjust them.

3.4. Running an Application with the Generated Image

Now, we have our custom JRE created by jlink.

To test our JRE, let's try to run our module by navigating inside the bin folder of our customjre directory and run the command below:

java --module jlinkModule/com.baeldung.jlink.HelloWorld

Again, the Windows shell, which we use, looks in the current directory for any executable before proceeding to the PATH. We need to pay extra attention to actually run our custom JRE, and not the java resolved against a PATH when we're on Linux or Mac.

4. Creating Custom JRE with Launcher Scripts

Optionally, we can also create a custom JRE with executable launcher scripts.

For this, we need to run the jlink command that has an extra –launcher parameter to create our launcher with our module and main class:

jlink --launcher customjrelauncher=jlinkModule/com.baeldung.jlink.HelloWorld
  --module-path "%JAVA_HOME%\jmods";out
  --add-modules jlinkModule
  --output customjre

This will generate two scripts: customjrelauncher.bat and customjrelauncher inside our customjre/bin directory.

Let's run the script:

customjrelauncher.bat

And the output will be:

Mar 18, 2019 12:34:21 AM com.baeldung.jlink.HelloWorld main
INFO: Hello World!

5. Conclusion

In this tutorial, we have learned how we can create a custom, modular JRE with jlink that only contains the bare minimum files needed for our module. We also looked into how to create a custom JRE with launcher scripts that can be easily executed and shipped.

Custom, modular Java runtime images are powerful. The goals for creating custom JREs are clear: they save on memory, improve performance, and also enhance security and maintainability. Lightweight custom JREs also enable us to create scalable applications for small devices.

The code snippets used in this tutorial are available over Github.