JDK 24 功能:JEP 493 JDK 瘦身革命,不用 JMOD 也能建立執行階段映像檔

JEP 493: Linking Run-Time Images without JMODs

在雲端運算與容器化技術盛行的今天,應用程式的部署效率與資源使用效率變得比以前更加重要。JDK 的安裝容量一直是持續討論的議題,特別是在需要頻繁下載、建置和部署容器映像的場景中。

Java 24JEP 493 允許開發者在不使用 JMOD 檔案的情況下,也能透過 jlink 工具建立客製化的執行階段映像,預期可為 JDK 減少約 25% 的體積,大幅提升容器部署效率。

前言

在現代雲端環境中,容器技術已成為應用程式部署的主流方式。包裝映像的檔案大小是我們需要關注的重要議題。無論是基底映像下載、映像儲存、上傳新版映像、部署環境等步驟,我們都希望能夠縮小映像檔的體積。

因為經常需要從容器註冊中心下載並快速複製映像檔,所以如果可以減少體積的話,就代表著能夠減少流量和儲存成本,並且縮短作業時間和提升部署效率。以 JDK 來說,目前完整的 JDK 主要包含兩個部分:

  1. 執行階段映像:可執行的 Java 執行環境
  2. 一組 JMOD 格式的模組:每個模組對應一個 JMOD 檔案,它們構成了上述執行階段映像,並且在使用 jlink 工具建立自訂執行階段映像時會被用到

什麼是 JMOD?

jmod 是自 Java 9 引入模組系統後新增的工具,其主要目的是為了提供更完整封裝 Java 模組的中介格式,以補足模組化的封裝和分發需求。傳統上,我們習慣使用 JAR 檔案封裝應用程式或函式庫,但 JAR 無法描述模組資訊(如 module-info.java),也無法包含原生程式碼或其他非類別檔案的資源。 jmod 就是為了解決這些限制而生,成為模組建構流程中的一環。

jmod 工具能建立 .jmod 檔案,它比 JAR 更廣泛地支援模組包裝需求。 JMOD 檔案可以包含以下內容:

  • .class 類別檔案(與 JAR 相同)
  • module-info.class(模組描述)
  • 原生函式庫(如 .so, .dll, .dylib 等等)
  • 原生命令、組態設定檔、其他資源檔案

我們可以將模組中的所有資源以單一檔案的形式進行封裝與管理,使模組化系統得以完整運作,同時也能讓 JDK 本身的模組分發保持一致。

使用 .jmod 格式有幾個顯著優勢。首先它能讓 Java 模組系統完整封裝所有資源,進一步支援以 jlink 產出輕量、專屬模組的可執行時期映像。其次 jmod 為內建模組提供一致的打包與分發機制,使整個模組系統更具整合性與可控性。此外,在企業環境中可精確控制模組的邊界與相依性,並有助於系統安全性與維運控管。

然而,儘管 .jmod 功能強大,但它也有一些實務限制。首先 .jmod 並非通用發行格式,無法透過類別路徑或模組路徑直接執行,因此無法取代 JAR 檔成為應用程式載體。其次 JMOD 檔較大且無壓縮,僅適合作為中介構建產物。另外它並不適合跨平台環境,因為它可能包含原生函式庫,所以需針對不同平台個別建立。因此 JMOD 檔更適合用於建構與部署流程中的中間步驟,而非最終發佈格式。

容器時代的瓶頸

事實上,一套完整 JDK 中的執行階段映像,就是透過 jlink 根據上述 JMOD 檔案所產生的映像結果。因此每一個類別檔案、原生函式庫、組態設定檔以及其他資源,其實都同時存在於 JMOD 檔案與執行階段映像之中。這對於檔案空間的使用來說,重複的檔案內容造成了巨大的浪費。

更具體地說,完整 JDK 中的 JMOD 檔案大約占據了整體大小的 25% 。在容器環境中,這樣的重複存儲不僅浪費了儲存空間,還會影響容器映像的下載與部署速度。

如果我們能夠讓 jlink 工具從執行階段映像本身提取類別檔案、原生函式庫、設定檔與其他資源,那麼我們就可以省略 JMOD 檔案,顯著減少已安裝 JDK 的體積。

JEP 493 概觀

JEP 493 的主要目標是讓 jlink 工具在不使用 JMOD 檔案的情況下,去建立自訂的執行階段映像(run-time image)以減少 JDK 約 25% 的體積

它允許開發者能夠從各種來源去產生執行階段映像,而不論這些模組是獨立的 JMOD 檔案模組化的 JAR 檔案,還是先前已經連結過的執行階段映像的一部分。

優點

  • 減少 JDK 約 25% 的安裝體積
  • 加快容器映像的下載與部署速度
  • 維持與現有 jlink 工具相同的使用體驗
  • 不影響現有的功能與相容性

缺點

  • 需要在建置 JDK 時特別啟用此功能。預設情況下並不會開啟,且某些 JDK 發行商可能選擇不啟用此功能
  • 無法產生包含 jlink 工具的執行階段映像
  • 不支援跨平台產生映像
  • 對修改過的設定檔案會有限制

介紹

建置時的新選項

JEP 493 引入了新的 JDK 建置選項 --enable-linkable-runtime,可用來建構一個能夠不依賴 JMOD 檔案就能使用 jlink 的 JDK。使用此選項所建構出來的 JDK 不會包含 JMOD 檔案,也就是說不會有 jmods 目錄。

這樣的 JDK 相較於使用預設組態建構的版本,體積大約縮小了 25% ,但仍然會包含相同的模組。下例為啟用本功能的方式,需要在建置 JDK 時使用新選項:

$ configure [ ... other options ... ] --enable-linkable-runtime
$ make images

所有版本的 jlink 工具都能處理 JMOD 檔案與模組化的 JAR 檔案。此外,在啟用了本功能來建置的 JDK 版本中,jlink 還能夠從自身所屬的執行階段映像中去讀取模組內容。我們可以使用 jlink --help 查看是否具備這項能力:

$ jlink --help
Usage: jlink <options> --module-path <module path> --add-modules <module> [,<module>...]

Capabilities:
      Linking from run-time image enabled

上述訊息中的 Linking from run-time image enabled 表示目前使用的 jlink 工具可以從其所屬的執行階段映像連結模組。如果顯示為 Linking from run-time image disabled 的話,就代表不支援該功能。

具備這項新能力的 jlink 工具在運作上會有以下行為:

  1. 如果模組路徑中有 JMOD 檔案,則仍會優先從 JMOD 檔案讀取模組;
  2. 當模組路徑中找不到 java.base 模組時,才會從目前的執行階段映像中載入;
  3. 其餘模組仍需用 --module-path 明確指定

使用方式不變

具有新能力的 jlink 其使用方式和過去完全相同。例如,如果要建立一個只包含 java.xmljava.base 模組的執行階段映像,可以照既有的方式操作,即使不提供任何 JMOD 檔案也沒問題:

$ jlink --add-modules java.xml --output image
$ image/bin/java --list-modules
java.base@24
java.xml@24
$

使用模組化 JAR 檔案

下面是一個更複雜的範例。如果我們要建立一個包含應用程式模組 app 與函式庫模組 lib 的映像,而它們是以模組化 JAR 檔案形式存在於 mlib 目錄中:

$ ls mlib
app.jar  lib.jar
$ jlink --module-path mlib --add-modules app --output app
$ app/bin/java --list-modules
app
lib
java.base@24

在本例中:

  • jlink 會從 app.jarlib.jar 中讀取模組的類別檔案與資源檔案
  • JDK 本身的模組(如 java.base)則從當前的執行階段映像中提取

查看模組來源

使用 --verbose 選項可以檢視每個模組的來源:

$ ls custom-jmods
foo.jmod
$ jlink --add-modules foo \\
        --module-path=custom-jmods \\
        --verbose \\
        --output foo-image
Linking based on the current run-time image
java.base jrt:/java.base (run-time image)
foo file:///path/to/custom-jmods/foo.jmod

Providers:
  java.base provides java.nio.file.spi.FileSystemProvider used by java.base

在本例中:

  • java.base 模組是從執行階段映像中提取(顯示為 jrt:/java.base
  • foo 是從指定的 foo.jmod 檔案讀取

這樣可以清楚地知道 jlink 使用了哪些來源來組裝最終的執行階段映像。

預設不啟用

現有 JDK 的預設建置組態將維持不變;也就是說,產出的 JDK 將仍會包含 JMOD 檔案,並且其中的 jlink 工具無法在沒有 JMOD 檔案的情況下運作。如果是從其他 JDK 發行商取得 JDK 的話,就要看該發行商決定是否要啟用本功能。

未來可能會預設啟用本功能。

限制

在使用 JEP 493 建置的 JDK 中,其 jlink 工具相較於預設版本有以下幾點限制:

由於 jlink 本身屬於 jdk.jlink 模組,因此在嘗試將其納入新的映像中時會失敗:

$ jlink --add-modules jdk.jlink --output image
Error: This JDK does not contain packaged modules and cannot be used \\
to create another run-time image that includes the jdk.jlink module

建置 JDK 的 conf 目錄包含了開發者可編輯的各種設定檔。例如 conf/security/java.security 檔案用來設定安全提供者、加密演算法等等:

  • 在預設建置的 JDK 中,jlink 是從 JMOD 檔案中複製這些設定檔
  • 在沒有 JMOD 檔的情況下,jlink 會直接從當前執行階段映像中複製設定檔

如果這些檔案與原始版本不一致,jlink 就會失敗,例如:

$ jlink --add-modules java.xml --output image
Error: [...]/bin/conf/security/java.security has been modified

這樣的限制可以避免將非預期或不安全的設定寫進新的執行階段映像中。例如,工具會限制我們修改設定,去啟用已禁用的過時雜湊演算法(message-digest algorithm)。這些修改過的設定並不適合複製進新映像之中。

三、無法跨平台產生映像

無法在 Linux / x64 上使用 jlink 建立用於 Windows / x64 的執行階段映像,或其他不同平台的交叉建立(Cross-linking)。

四、不支援 --patch-module

如果目前的執行階段映像本身是使用 --patch-module 選項啟動的話,則 jlink 不支援從該映像連結模組。

五、無法從其他執行階段映像提取模組

例如,目前無法指定另一個執行階段映像作為 --module-path 來源。

以上的限制設計主要是為了安全性與一致性,並確保 jlink 的輸出能符合預期的可靠性標準。

替代方案

未來 JDK 發行商可以選擇將 JDK 的 JMOD 檔案作為額外下載項目提供。某些 Linux 發行版事實上已採用類似做法,它們提供一個安裝套件作為安裝 JDK 時的執行階段映像,另提供一個套件來安裝對應的 JMOD 檔案。

然而,這種做法相當脆弱,因為若第二個套件未安裝,則 jlink 工具將無法運作。此外,這種方式不適合用於雲端環境,因為在雲端中, JDK 執行階段映像與其 JMOD 檔案可能會被打包進不同、甚至互相衝突的容器映像層,導致錯誤與不一致的情況。

總結

Java 24 的 JEP 493 為 Java 平台在容器時代提供了重要的優化方案。透過去除冗餘的 JMOD 檔案,不僅能顯著減少 JDK 的體積,更能提升容器化應用程式的部署效率。雖然目前這項功能預設並未啟用,但它代表了 Java 平台在適應現代部署需求上的重要進展。

隨著雲端運算與容器技術的持續發展,我們可以期待這項功能在未來版本中預設全面啟用,為 Java 開發人員提供更優質的開發與部署體驗。同時,針對目前的一些限制,如跨平台建立映像的需求,相信也會在後續版本中得到改善。

本篇文章的內容為老喬原創、二創或翻譯而來。雖已善盡校對、順稿與查核義務,但人非聖賢,多少仍會有疏漏之處難以避免。如果大家有任何問題、建議或指教,都歡迎在底下留言與老喬討論!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

1 × five =

目錄
返回頂端