高版本jdk的模块化与自定义jre

1. 概述

*jlink* 是一种工具,用于生成自定义 Java 运行时映像,该映像仅包含给定应用程序所需的平台模块。

这样的运行时镜像与 JRE 完全相同,但仅包含我们选择的模块以及它们运行所需的依赖项。模块化运行时映像的概念是在 JEP 220 中引入的。

在本教程中,我们将学习如何使用 jlink 创建自定义 JRE,我们还将运行和测试我们的模块在 JRE 中是否正常运行。

2. 需要创建自定义 JRE

让我们通过一个示例来了解自定义运行时映像背后的动机。

我们将创建一个简单的模块化应用程序。

首先,让我们创建一个 HelloWorld 类和相应的模块:

mkdir java && cd java

创建 module-info.java

module moduleDemo {
    requires java.logging;
}

创建包

mkdir -p com/bingbaihanji/jlink && cd com/bingbaihanji/jlink

创建 HelloWorld.java

package com.bingbaihanji.jlink;

import java.util.logging.Logger;

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

项目结构如下

.
├── com
│   └── bingbaihanji
│       └── jlink
│           └── HelloWorld.java
└── module-info.java

要运行这个程序,我们只需要 *HelloWorld、*StringLoggerObject 类。

尽管这个程序只需要四个类来运行,但 JRE 中的所有预定义类也会被执行,即使我们的程序不需要它们。

所以,要运行一个小程序,就得维护一个完整的 JRE,简直就是浪费内存。

因此,自定义的 JRE 是运行示例的最佳选择。

使用 *jlink*,我们可以创建自己的小型 JRE,其中仅包含我们想要使用的相关类,而不会浪费内存,因此,我们将看到性能的提高。

3. 构建自定义 Java 运行时映像

我们将执行一系列简单的步骤来创建自定义 JRE 映像。

3.1. 编译模块

首先,让我们从命令行编译上面提到的程序:

编译 module-info.java

javac -d out module-info.java

编译 HelloWorld.java

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

运行该项目:

java --module-path out --module moduleDemo/com.bingbaihanji.jlink.HelloWorld

输出结果

12月 15, 2024 3:08:23 下午 com.bingbaihanji.jlink.HelloWorld main
信息: Hello World!

3.2. 使用 jdeps 列出依赖模块

为了使用 jlink,我们需要知道应用程序使用的 JDK 模块列表,我们应该将其包含在自定义 JRE 中。

让我们使用 jdeps 命令来获取应用程序中使用的依赖模块:

jdeps --module-path out -s --module moduleDemo

结果

moduleDemo -> java.base
moduleDemo -> java.logging

这很合理,因为 java.base 是 Java 代码库所需的最小模块,而 java.logging 由我们程序中的 Logger 使用。

要为基于模块的应用程序创建自定义 JRE,我们可以使用 jlink 命令。以下是它的基本语法:

jlink --help
用法: jlink <选项> --module-path <模块路径> --add-modules <模块>[,<模块>...]

现在,让我们使用 Java 21 为我们的程序创建自定义 JRE

bash:

jlink --module-path $JAVA_HOME/jmods:out \
      --add-modules moduleDemo \
      --output custom-jre

PowerShell:

jlink --module-path "$env:JAVA_HOME\jmods;out" `
      --add-modules moduleDemo `
      --output custom-jre

其中

--module-path

指定模块的搜索路径,jlink 会在这些路径中查找需要打包的模块。
模块路径可以包括:

  • JDK 提供的标准模块文件(通常位于 $JAVA_HOME/jmods)。
  • 用户定义的模块(如你项目的 out 目录)

--add-modules

指定要打包的模块列表。
jlink 会从指定的模块开始,并自动分析模块依赖,将依赖的模块一起打包到自定义 JRE 中。

--add-modules moduleDemo

--output

指定生成的自定义 JRE 的输出目录。

示例

plaintext


复制代码
--output custom-jre
  • custom-jre 是输出目录名,你可以随意命名。
  • 执行后,custom-jre 目录下将包含自定义 JRE 的所有文件和结构,如 binlib 等。

完成后会在当前目录产生自定义项目的jre custom-jre

其他扩展选项

--compress

指定压缩级别以减少自定义 JRE 的大小:

  • --compress=0:不压缩(默认)。
  • --compress=1:常量池字符串共享。
  • --compress=2:常量池字符串共享并进行 ZIP 压缩。

3.4. 使用生成的映像运行应用程序

.\custom-jre\bin\java.exe --module-path out --module moduleDemo/com.bingbaihanji.jlink.HelloWorld

结果:

Dec 15, 2024 3:23:40 PM com.bingbaihanji.jlink.HelloWorld main
信息: Hello World!

5. 总结

使用 jlink 创建自定义的模块化 JRE,该 JRE 仅包含模块所需的最少文件。

自定义的模块化 Java 运行时映像非常强大。创建自定义 JRE 的目标很明确:它们可以节省内存、提高性能,还可以增强安全性和可维护性。 轻量级自定义 JRE 还使我们能够为小型设备创建可扩展的应用程序。

人生不作安期生,醉入东海骑长鲸