Java 9 揭秘(7. 創建自定義運行時映像)


Tips
做一個終身學習的人。

Java 9
在第一章節中,主要介紹以下內容:

  • 什么是自定義運行時映像和JIMAGE格式
  • 如何使用jlink工具創建自定義的運行時映像
  • 如何指定命令名稱來運行存儲在自定義映像中的應用程序
  • 如何使用jlink工具插件

一. 什么是自定義運行時映像?

在JDK 9之前,Java運行時映像可用作巨大整體的單體(artifact),從而增加了下載時間,啟動時間和內存占用。單體JRE使得不可能在具有小內存的設備上使用Java。 如果將Java應用程序部署到雲端,則需要支付使用的內存; 最常見的是,單體JRE使用的內存比所要求的內存還要多,因此為雲服務支付更多的內存。 在Java 8中引入的Compact配置文件,以減少JRE大小,從而減少運行時內存占用 —— 通過允許將JRE的一個子集打包在稱為Compact配置文件的自定義運行時映像中。

Java 9采用了整體的方法來打包運行時映像。 所有平台代碼都已經模塊化了。 你的應用程序代碼也打包模塊化了。 在Java 9中,可以創建一個自定義運行時,它將包含應用程序模塊和應用程序所使用的平台模塊。 還可以在運行時映像中打包本地命令。 創建運行時映像的另一個好處是,你只需將一個包——運行時映像——發送給你的應用程序用戶,而不需要下載並安裝單獨的JRE軟件包來運行應用程序。

運行時映像以特定格式存儲,稱為JIMAGE,該格式針對空間和速度進行了優化。 僅在運行時支持JIMAGE格式。 它是用於在JDK中存儲和索引模塊,類和資源的容器格式。 從JIMAGE文件搜索和加載類比從JAR和JMOD文件快很多。 JIMAGE格式是JDK內部的,開發人員很少需要直接與JIMAGE文件進行交互。

預計JIMAGE格式將隨着時間的推移而不斷發展,因此其內部部件不會面向開發人員。 JDK 9附帶了一個名為jimage的工具,可用於瀏覽JIMAGE文件。

Tips
可以使用jlink工具來創建一個運行時映像,它使用一種名為JIMAGE的新文件來存儲模塊。 JDK 9附帶jimage工具,可以瀏覽JIMAGE文件的內容。

如果你的代碼期望將運行時映像存儲在名為rt.jar文件的文件中,請謹慎。 JDK運行庫存儲在JDK 9之前的rt.jar文件中,但在JDK 9中不再是這樣。當將應用程序遷移到JDK 9時,可能會破壞你的代碼。

二. 創建自定義運行時映像

可以使用jlink工具創建特定於平台的運行時映像。 運行時映像將包含指定的應用程序模塊和只需的平台模塊,從而減少運行時映像的大小。 這對於在具有少量內存的嵌入式設備上運行的應用程序非常有用。 JDK 9附帶了jlink工具。 它位於JDK_HOME\bin目錄中。 運行jlink工具的一般語法如下:

jlink <options> --module-path <modulepath> --add-modules <mods> --output <path>

在這里,<options>包括jlink的零個或多個選項,如下面表格所示,<modulepath>是平台和應用程序模塊所在的模塊路徑以添加到映像中。 模塊可以是模塊化的JAR,展開目錄和JMOD文件。 <mods>是要添加到映像的模塊的列表,這可能會導致添加其他模塊,因為其他模塊的傳遞依賴關系。 <path>是生成的運行時映像被存儲的輸出目錄。

選項 描述
--add-modules <mod>,<mod>... 指定要解析的根模塊列表。 所有已解析的模塊將被添加到運行時映像中。
--bind-services 在鏈接過程中執行完整的服務綁定。 如果添加的模塊包含uses語句,jlink將掃描模塊路徑上的所有JMOD文件,包括在uses語句中指定的服務運行時映像中的所有服務提供者的模塊。
-c, --compress <0 OR 1 OR 2>[:filter=<pattern-list>] 指定輸映像中所有資源的壓縮級別。 0表示常量字符串共享,1表示ZIP,2表示兩者。 可以指定可選的<pattern-list>過濾列出要包括的文件的模式。
--disable-plugin <plugin-name> 禁用指定的插件。
--endian <little OR big> 指定生成的運行時映像的字節指令。 默認值是本地平台的字節指令。
-h,--help 打印使用說明和jlink工具的所有選項列表。
--ignore-signing-information 當簽名的模塊化JAR鏈接在映像中時,抑制致命錯誤。 與簽名的模塊化JAR的相關的簽名文件不會復制到運行時映像。
--launcher <command>=<module> 指定模塊的啟動器命令。 <command>是要生成以啟動應用程序的命令的名稱,例如runmyapp。 該工具將創建一個腳本或批處理文件,<command>以運行<module>中的主類。
--launcher <command>=<module>/<main-class> 指定模塊和主類的啟動器命令。 <command>是要生成以啟動應用程序的命令的名稱,例如runmyapp。 該工具將創建一個腳本/批處理文件,<command>以運行<module>中的<main-class>
--limit-modules <mod>,<mod> 將可觀察模塊限制在命名模塊的傳遞性關閉主模塊(如果指定)以及使用--add-modules選項指定的任何其他模塊中。
--list-plugins 列出可用的插件。
-p, --module-path <modulepath> 指定找到將平台和應用程序模塊添加到運行時映像的模塊路徑。
--no-header-files 排除本地代碼的include頭文件。
--no-man-pages 排除手冊主頁。
--output <path> 指定要復制運行時映像的目錄。
--save-opts <filename> 將jlink選項保存在指定的文件中。
-G, --strip-debug 從輸出映像中查找調試信息。
--suggest-providers [<service-name>,...] 如果沒有指定服務名稱,它會建議將為添加的模塊鏈接的所有服務的提供程序的名稱。如果指定一個或多個服務名稱,它會建議指定服務名稱的提供者。 在創建映像之前,可以使用此選項,以了解在使用--bind-services選項時將包括哪些服務。
-v, --verbose 打印詳細輸出。
--version 打印jlink工具的版本。
@<filename> 從指定的文件讀取選項。

讓我們創建一個運行時映像,其中包含素數檢查應用程序和所需平台模塊的四個模塊,其中僅包含java.base模塊。 請注意,以下命令僅包含素數檢查程序應用程序中的三個模塊。 第四個將被添加,因為這三個依賴於第四個模塊。 命令后面的文本詳細解釋了這一點。

C:\Java9Revealed>jlink --module-path jmods;C:\java9\jmods
  --add-modules com.jdojo.prime.client,com.jdojo.prime.generic,com.jdojo.prime.faster
  --launcher runprimechecker=com.jdojo.prime.client
  --output primechecker

在解釋此命令的所有選項之前,讓我們驗證運行時映像是否已成功創建。 該命令應該將運行時映像復制到C:\ Java9Revealed\primechecker文件夾。 運行以下命令以驗證運行時映像包含五個模塊:

C:\Java9Revealed>primechecker\bin\java --list-modules

輸出結果為:

com.jdojo.prime@1.0
com.jdojo.prime.client@1.0
com.jdojo.prime.faster@1.0
com.jdojo.prime.generic@1.0
java.base@9-ea

如果你獲得的輸出類似於此處所示,運行時映像已正確創建。 在輸出中的@符號之后顯示的模塊版本號可能與你的有所不同。

--module-path選項指定兩個目錄,jmods和C:\ java9\jmods。 在C:\ Java9Revealed\jmods目錄中保存了素數檢查程序的四個JMOD文件。 模塊路徑中的第一個元素允許jlink工具查找所有應用程序模塊。 將JDK 9安裝在C:\java9目錄下,所以模塊路徑中的第二個元素讓工具找到平台模塊。 如果沒有指定第二部分,則會出現錯誤:

Module java.base not found.

--add-modules選項指定素數檢查程序應用程序的三個模塊。 可能會想知道為什么我們沒有使用此選項指定第四個模塊com.jdojo.prime。 此列表包含根模塊,而不僅僅包含在運行時映像中的模塊。 jlink工具將解決所有這些根模塊的依賴關系,並將所有已解析的依賴模塊包含在運行時映像中。 這三個模塊取決於com.jdojo.prime模塊,它將通過將其定位在模塊路徑中來解析,因此將被包含在運行時映像中。 該映像還將包含java.base模塊,因為所有應用程序模塊都隱含依賴於它。

--output選項指定運行時映像將被復制的目錄。 該命令將運行時映像復制到C:\Java9Revealed\primechecker目錄。 輸出目錄包含以下子目錄和名為release的文件:

  • bin
  • conf
  • include
  • legal
  • lib

bin目錄包含可執行文件。 在Windows上,它還包含動態鏈接的本地類庫(.dll文件)。
conf目錄包含可編輯的配置文件,如.properties和.policy文件。
include目錄包含C / C ++頭文件。
legal目錄包含法律聲明。
lib目錄包含添加到運行時映像的模塊,以及其他文件。 在Mac,Linux和Solaris上,它還將包含系統的動態鏈接本地類庫。

使用--launcher選項與jlink命令。 指定了命令名稱runprimechecker,模塊名稱為com.jdojo.prime.client--launcher選項使jlink在bin目錄中的Windows上創建一個平台特定的可執行文件,例如runprimechecker.bat文件。 可以使用此可執行文件來運行你的應用程序。 文件內容只是在這個模塊中運行主類的包裝器。 可以使用此文件來運行應用程序:

C:\Java9Revealed> primechecker\bin\runprimechecker

輸出結果為:

Using jdojo.faster.primechecker:
3 is a prime.
4 is not a prime.
121 is not a prime.
977 is a prime.
Using jdojo.faster.primechecker:
3 is a prime.
4 is not a prime.
121 is not a prime.
977 is a prime.
A PrimeChecker service provider with the name 'jdojo.probable.primechecker' was not found.

還可以使用java命令來啟動應用程序,使用jlink工具已經將文件復制到bin目錄下:

C:\Java9Revealed>primechecker\bin\java --module com.jdojo.prime.client

此命令的輸出將與上一個命令的輸出相同。 請注意,不必指定模塊路徑。 鏈接器jlink工具在創建運行時映像時處理模塊路徑。 當運行生成的運行時映像的java命令時,它會知道在哪里找到模塊。 還要注意,不必為命令指定主類名稱。 剛才指定了模塊名稱。 已經設置了com.jdojo.prime.client模塊的main-class屬性。 當運行模塊而不指定主類時,該模塊的module-info.class文件中設置的main-class屬性將用作主類。

三. 綁定服務

在上一節中,為素數服務客戶端應用程序創建了運行時映像。 必須使用要包含在映像中的--add-modules選項指定所有服務提供者模塊的名稱。 在本節中,將展示如何在使用jlink工具使用--bind-services選項創建運行時映像時自動綁定服務。 這一次,需要將模塊(即com.jdojo.prime模塊)添加到模塊圖中,並且jlink工具將負責其余部分。 com.jdojo.prime.client模塊讀取com.jdojo.prime模塊,因此將前者添加到模塊圖中也將解決后者。 以下命令打印運行時映像的建議服務提供程序列表。

C:\Java9Revealed>jlink --module-path jmods;C:\java9\jmods
--add-modules com.jdojo.prime.client
--suggest-providers

以下是部分輸出內容:

module com.jdojo.prime located (file:///C:/Java9Revealed/jmods/com.jdojo.prime.jmod)
    uses com.jdojo.prime.PrimeChecker
module com.jdojo.prime.client located (file:///C:/Java9Revealed/jmods/com.jdojo.prime.client.jmod)
module java.base located (file:///C:/java9/jmods/java.base.jmod)
    uses java.lang.System$LoggerFinder
    uses java.net.ContentHandlerFactory
...
Suggested providers:
  module com.jdojo.prime.faster provides com.jdojo.prime.PrimeChecker, used by com.jdojo.prime
  module com.jdojo.prime.generic provides com.jdojo.prime.PrimeChecker, used by com.jdojo.prime
  module com.jdojo.prime.probable provides com.jdojo.prime.PrimeChecker, used by com.jdojo.prime
  module java.desktop provides java.net.ContentHandlerFactory, used by java.base
 ...

該命令僅將com.jdojo.prime.client模塊指定給--add-modules選項。 com.jdojo.primejava.base模塊被解析,因為com.jdojo.prime.client模塊讀取它們。 掃描所有已解析的模塊的uses語句,隨后掃描模塊路徑中的所有模塊,以使用在uses語句中指定的服務的服務提供者。 所有找到的服務提供者都被打印出來。

Tips
可以為--suggest-providers選項指定參數。 如果沒有參數使用它,請確保在命令結束時指定它。 否則,--suggest-providers選項之后的選項將被解釋為其參數,將收到錯誤。

以下命令將com.jdojo.prime.PrimeChecker指定為--suggest-providers選項的服務名稱,以打印為此服務找到的所有服務提供者:

C:\Java9Revealed>jlink --module-path jmods;C:\java9\jmods
--add-modules com.jdojo.prime.client
--suggest-providers com.jdojo.prime.PrimeChecker

輸出結果:

Suggested providers:
  module com.jdojo.prime.faster provides com.jdojo.prime.PrimeChecker, used by com.jdojo.prime
  module com.jdojo.prime.generic provides com.jdojo.prime.PrimeChecker, used by com.jdojo.prime
  module com.jdojo.prime.probable provides com.jdojo.prime.PrimeChecker, used by com.jdojo.prime

使用與前述相同的邏輯,找到所有三個服務提供者。 讓我們創建一個包含所有三個服務提供者的新的運行時映像。 以下命令執行該操作:

C:\Java9Revealed>jlink --module-path jmods;C:\java9\jmods
--add-modules com.jdojo.prime.client
--launcher runprimechecker=com.jdojo.prime.client
--bind-services
--output primecheckerservice

將此命令與上一節中使用的命令進行比較。 這次,只使用--add-modules選項指定了一個模塊。 也就是說,不必指定服務提供者模塊的名稱。 使用了--bind-services選項,因此添加的模塊中的所有服務提供者引用都將自動添加到運行時映像。 指定了一個名為primecheckerservice的新輸出目錄。 以下命令運行新創建的運行時映像:

C:\Java9Revealed>primecheckerservice\bin\runprimechecker

以下是輸出結果:

Using jdojo.generic.primechecker:
3 is a prime.
4 is not a prime.
121 is not a prime.
977 is a prime.
Using jdojo.faster.primechecker:
3 is a prime.
4 is not a prime.
121 is not a prime.
977 is a prime.
Using jdojo.probable.primechecker:
3 is a prime.
4 is not a prime.
121 is not a prime.
977 is a prime.

輸出證明,模塊路徑中的所有三個素數檢查服務提供者都自動添加到運行時映像中。

四. 使用jlink工具插件

jlink工具使用插件架構來創建運行時映像。 它將所有類,本地類庫和配置文件收集到一組資源中。 它構建了一個轉換器管道,它們是指定為命令行選項的插件。 資源進入管道。 管道中的每個轉換器對資源進行某種變換,並將變換的資源輸送到下一個轉換器。 最后,jlink將轉換的資源提供給映像構建器。

JDK 9為jlink工具附帶了幾個插件。 這些插件定義了命令行選項。 要使用插件,需要使用命令行選項。 可以使用--list-plugins選項運行jlink工具,使用其描述和命令行選項打印所有可用插件的列表:

C:\Java9Revealed>jlink --list-plugins

以下是輸出結果:

List of available plugins:
Plugin Name: class-for-name
Option: --class-for-name
Description: Class optimization: convert Class.forName calls to constant loads.
Plugin Name: compress
Option: --compress=<0|1|2>[:filter=<pattern-list>]
Description: Compress all resources in the output image.
Level 0: constant string sharing
Level 1: ZIP
Level 2: both.
An optional <pattern-list> filter can be specified to list the pattern of
files to be included.
Plugin Name: dedup-legal-notices
Option: --dedup-legal-notices=[error-if-not-same-content]
Description: De-duplicate all legal notices.  If error-if-not-same-content is
specified then it will be an error if two files of the same filename
are different.
Plugin Name: exclude-files
Option: --exclude-files=<pattern-list> of files to exclude
Description: Specify files to exclude. e.g.: **.java,glob:/java.base/native/client/**
Plugin Name: exclude-jmod-section
Option: --exclude-jmod-section=<section-name>
where <section-name> is "man" or "headers".
Description: Specify a JMOD section to exclude
Plugin Name: exclude-resources
Option: --exclude-resources=<pattern-list> resources to exclude
Description: Specify resources to exclude. e.g.: **.jcov,glob:**/META-INF/**
Plugin Name: generate-jli-classes
Option: --generate-jli-classes=@filename
Description: Takes a file hinting to jlink what java.lang.invoke classes to pre-generate. If
this flag is not specified a default set of classes will be generated.
Plugin Name: include-locales
Option: --include-locales=<langtag>[,<langtag>]*
Description: BCP 47 language tags separated by a comma, allowing locale matching
defined in RFC 4647. e.g.: en,ja,*-IN
Plugin Name: order-resources
Option: --order-resources=<pattern-list> of paths in priority order.  If a @file
is specified, then each line should be an exact match for the path to be ordered
Description: Order resources. e.g.: **/module-info.class,@classlist,/java.base/java/lang/**
Plugin Name: release-info
Option: --release-info=<file>|add:<key1>=<value1>:<key2>=<value2>:...|del:<key list>
Description: <file> option is to load release properties from the supplied file.
add: is to add properties to the release file.
Any number of <key>=<value> pairs can be passed.
del: is to delete the list of keys in release file.
Plugin Name: strip-debug
Option: --strip-debug
Description: Strip debug information from the output image
Plugin Name: strip-native-commands
Option: --strip-native-commands
Description: Exclude native commands (such as java/java.exe) from the image
Plugin Name: system-modules
Option: --system-modules
Description: Fast loading of module descriptors (always enabled)
Plugin Name: vm
Option: --vm=<client|server|minimal|all>
Description: Select the HotSpot VM in the output image.  Default is all
For options requiring a <pattern-list>, the value will be a comma separated
list of elements each using one the following forms:
  <glob-pattern>
  glob:<glob-pattern>
  regex:<regex-pattern>
  @<filename> where filename is the name of a file containing patterns to be
              used, one pattern per line

以下命令使用compressstrip-debug插件。 壓縮插件將壓縮映像,這將得到較小的映像。 這里使用壓縮級別2來進行最大壓縮。 strip-debug插件從Java代碼中刪除調試信息,從而進一步減小映像的大小。 在運行此命令之前,請確保刪除先前創建的primechecker目錄。

C:\Java9Revealed>jlink --module-path jmods;C:\java9\jmods
  --compress 2
  --strip-debug
  --add-modules com.jdojo.prime.client,com.jdojo.prime.generic,com.jdojo.prime.faster
  --launcher runprimechecker=com.jdojo.prime.client
  --output primechecker

Tips
目前插件API是完全實驗性的,並且未定義插件的執行順序。 在早期的實現中,jlink工具還支持定制插件,后來被刪除。

五. jimage 工具

Java運行時在JIMAGE文件中運送模塊運行時映像。 該文件名為modules,它位於JAVA_HOME\lib中,其中JAVA_HOME可以是JDK_HOME或JRE_HOME。 jimage工具用於瀏覽JIMAGE文件的內容。 它可以:

  • 從JIMAGE文件中提取條目
  • 打印存儲在JIMAGE中的內容的摘要
  • 打印其名稱,大小,偏移量等條目列表。
  • 驗證類文件

jimage工具存儲在JDK_HOME\bin目錄中。 命令的一般格式如下:

jimage <subcommand> <options> <jimage-file-list>

這里,<subcommand>是下面第一個表格列出的子命令之一。 <options>是第二個表格列出的一個或多個選項;<jimage-file-list>是一個空格分隔的JIMAGE文件列表。

子命令 描述
extract 從指定的JIMAGE文件中將所有條目解壓縮到當前目錄。 使用--dir選項為提取的條目指定另一個目錄。
info 打印包含在指定JIMAGE文件頭部的詳細信息。
list 在指定的JIMAGE文件中打印所有模塊及其條目的列表。 使用--verbose選項包括條目的詳細信息,例如其大小,偏移量以及條目是否被壓縮。
verify 在指定的JIMAGE文件中打印驗證不是類的.class條目列表。
選項 描述
-dir <dir-name> 指定提取子命令的目標目錄,其中將提取JIMAGE文件中的條目。
-h, --help 打印jimage工具的使用信息。
--include <pattern-list> 指定過濾條目的模式列表。 模式列表的值是以逗號分隔的元素列表,每個元素使用以下形式之一: <glob-pattern> glob:<glob-pattern> regex:<regex-pattern>
--full-version 打印jimage工具的完整版本信息。
--verbose 當與列表子命令一起使用時,打印詳細信息,如大小,偏移量和壓縮級別。
--version 打印jimage工具的版本信息。

舉幾個使用jimage命令的例子。 示例使用保存在C:\java9\lib\modules上的JDK 9運行時映像。 當運行這些示例時,將需要將其替換為你的映像位置。 還可以使用這些示例中由jlink工具創建的任何自定義運行時映像。

以下命令從運行時映像中提取所有條目,並將其復制到extracted_jdk目錄。 該命令需要幾秒鍾才能完成。

C:\Java9Revealed>jimage extract --dir extracted_jdk C:\java9\lib\modules

以下命令將以.png擴展名的所有圖像條目從JDK運行時映像提取到extracted_images目錄中:

C:\Java9Revealed>jimage extract --include regex:.+\.png --dir extracted_images C:\java9\lib\modules

以下命令列出運行時映像中的所有條目。 顯示部分輸出:

C:\Java9Revealed>jimage list C:\java9\lib\modules

以下是部分輸出內容:

jimage: C:\java9\lib\modules
Module: java.activation
    META-INF/mailcap.default
    META-INF/mimetypes.default
...
Module: java.annotations.common
    javax/annotation/Generated.class
...

以下命令列出運行時映像中的所有條目以及條目的詳細信息。 請注意使用--verbose選項。

C:\Java9Revealed>jimage list --verbose C:\java9\lib\modules

以下是部分輸出:

jimage: C:\java9\lib\modules
Module: java.activation
Offset     Size   Compressed Entry
34214466    292            0 META-INF/mailcap.default
34214758    562            0 META-INF/mimetypes.default
...
Module: java.annotations.common
Offset     Size   Compressed Entry
34296622    678            0 javax/annotation/Generated.class
...

以下命令打印無效的類文件列表。 你可能會想知道你如何使類文件無效。 通常,你不會有一個無效的類文件 —— 但黑客會! 但是,要運行此示例,需要一個無效的類文件。 可以使用一個簡單的想法 —— 拿一個有效的類文件,在文本編輯器中打開它,並部分和隨機地刪除其內容,使其成為無效的類文件。 將編譯的類文件的內容復制到Main2.class文件中,並刪除了其中的一些內容,使其成為無效的類。 將Main2.class文件添加到與Main.class相同目錄中的com.jdojo.prime.client模塊中。 使用上一個命令為此示例的素數檢查應用程序重新創建了運行時映像。 如果使用JDK附帶的Java運行時映像,則不會看到任何輸出,因為JDK運行時映像中的所有類文件都有效。

C:\Java9Revealed>jimage verify primechecker\lib\modules

會得到以下錯誤信息:

jimage: primechecker\lib\modules
Error(s) in Class: /com.jdojo.prime.client/com/jdojo/prime/client/Main2.class

六. 總結

在JDK 9中,運行時映像以特定格式保存,稱為JIMAGE,該格式針對空間和速度進行了優化。 僅在運行時支持JIMAGE格式。 它是用於在JDK中存儲和索引模塊,類和資源的容器格式。 從JIMAGE文件搜索和加載類比從JAR和JMOD文件快很多。 JIMAGE格式是JDK內部的,開發人員很少需要直接與JIMAGE文件進行交互。

它附帶了一個名為jlink的工具,可為應用程序創建一個JIMAGE格式的運行時映像,該應用程序將包含應用程序模塊和應用程序所使用的那些平台模塊。 jlink工具可以從存儲在模塊JAR,展開的目錄和JMOD文件中的模塊創建運行時映像。 JDK 9附帶了一個名為jimage的工具,可用於瀏覽JIMAGE文件的內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM