- 適用於 JDK 23 的 GraalVM (最新)
- 適用於 JDK 24 的 GraalVM (搶先體驗)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發版本
使用追蹤代理程式收集中繼資料
Native Image 工具依賴於應用程式在執行階段可達程式碼的靜態分析。但是,分析無法始終完全預測 Java 原生介面 (JNI)、Java 反射、動態 Proxy 物件或類別路徑資源的所有用法。這些動態功能的未偵測到的用法必須以中繼資料(以程式碼預先計算或以 JSON 組態檔的形式)提供給 native-image
工具。
在這裡,您將找到有關如何自動收集應用程式的中繼資料並寫入 JSON 組態檔的資訊。若要瞭解如何在程式碼中計算動態功能呼叫,請參閱可達性中繼資料。
目錄 #
追蹤代理程式 #
GraalVM 提供追蹤代理程式,可輕鬆收集中繼資料並準備組態檔。代理程式會追蹤一般 Java VM 上應用程式執行期間動態功能的所有用法。
使用 GraalVM JDK 中的 java
命令在命令列上啟用代理程式
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ ...
注意:
-agentlib
必須在-jar
選項或類別名稱或任何應用程式參數之前指定,作為java
命令的一部分。
執行時,代理程式會尋找 native-image
工具需要額外資訊的類別、方法、欄位、資源。當應用程式完成且 JVM 結束時,代理程式會將中繼資料寫入指定輸出目錄 (/path/to/config-dir/
) 中的 JSON 檔案。
可能需要多次執行應用程式 (使用不同的執行路徑),以改善動態功能的涵蓋範圍。config-merge-dir
選項會新增至現有的組態檔集,如下所示
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=/path/to/config-dir/ ... ^^^^^
代理程式還提供以下選項,可定期寫入中繼資料
config-write-period-secs=n
:每隔n
秒寫入中繼資料檔案;n
必須大於 0。config-write-initial-delay-secs=n
:等待n
秒才首次寫入中繼資料;預設值為1
。
例如
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/,config-write-period-secs=300,config-write-initial-delay-secs=5 ...
上述命令將在初始延遲 5 秒後,每 300 秒將中繼資料檔案寫入 /path/to/config-dir/
。
建議手動檢查產生的組態檔。由於代理程式僅觀察執行的程式碼,因此應用程式輸入應涵蓋盡可能多的程式碼路徑。
產生的組態檔可以透過將其放置在類別路徑上的 META-INF/native-image/
目錄中,提供給 native-image
工具。此目錄(或其任何子目錄)會搜尋名為 reachability-metadata.json 的檔案,然後該檔案會自動包含在建置過程中。並非所有這些檔案都必須存在。當找到多個具有相同名稱的檔案時,會考慮所有檔案。
若要測試代理程式在範例應用程式上收集中繼資料,請前往使用反射建置原生可執行檔指南。
條件式中繼資料收集 #
代理程式可以根據執行程式碼中的用法推斷中繼資料條件。條件式中繼資料主要針對程式庫維護人員,目標是減少整體佔用空間。
若要使用代理程式收集條件式中繼資料,請參閱條件式中繼資料收集。
代理程式進階用法 #
基於呼叫端的篩選器 #
依預設,代理程式會篩選 Native Image 在沒有組態的情況下支援的動態存取。篩選機制透過識別執行存取的 Java 方法(也稱為呼叫端方法),並將其宣告類別與一系列篩選規則比對來運作。內建篩選規則會排除源自 JVM 或 Native Image 直接支援的 Java 類別程式庫部分 (例如 java.nio
) 中的動態存取,這些動態存取會從產生的組態檔中排除。正在存取的項目(類別、方法、欄位、資源等)與篩選無關。
除了內建篩選器外,還可以使用 caller-filter-file
選項指定具有其他規則的自訂篩選器檔案。例如:-agentlib:caller-filter-file=/path/to/filter-file,config-output-dir=...
篩選器檔案具有以下結構
{ "rules": [
{"excludeClasses": "com.oracle.svm.**"},
{"includeClasses": "com.oracle.svm.tutorial.*"},
{"excludeClasses": "com.oracle.svm.tutorial.HostedHelper"}
],
"regexRules": [
{"includeClasses": ".*"},
{"excludeClasses": ".*\\$\\$Generated[0-9]+"}
]
}
rules
區段包含一系列規則。每個規則都會指定 includeClasses
,這表示來自比對類別的查詢將包含在產生的組態中,或指定 excludeClasses
,這會將來自比對類別的查詢從組態中排除。每個規則都會定義一個模式以比對類別。模式可以以 .*
或 .**
結尾,解譯如下:- .*
比對套件中的所有類別,而且只比對該套件;- .**
比對套件中的所有類別,以及任何深度的所有子套件。如果沒有 .*
或 .**
,則該規則僅適用於與模式比對的具有完整名稱的單一類別。所有規則都會依指定的順序處理,因此後續規則可以部分或完全覆寫先前的規則。當提供多個篩選器檔案時(透過指定多個 caller-filter-file
選項),它們的規則會依指定檔案的順序連結在一起。內建呼叫端篩選器的規則一律會先處理,因此可以在自訂篩選器檔案中覆寫這些規則。
在上述範例中,第一個規則會從產生的中繼資料中排除所有來自套件 com.oracle.svm
及其所有子套件(及其子套件等)的類別的查詢。但是,在下一個規則中,再次包含直接位於套件 com.oracle.svm.tutorial
中的那些類別的查詢。最後,再次排除來自 HostedHelper
類別的查詢。這些規則中的每一項都會部分覆寫先前的規則。例如,如果規則順序相反,則排除 com.oracle.svm.**
將是最後一個規則,並會覆寫所有其他規則。
regexRules
區段也包含一系列規則。其結構與 rules
區段的結構相同,但規則指定為正規表示式模式,這些模式會比對整個完整類別識別碼。regexRules
區段為選用。如果指定了 regexRules
區段,則當 (且僅當) rules
和 regexRules
都包含該類別,且兩者都未排除該類別時,才視為已包含該類別。如果沒有 regexRules
區段,則只有 rules
區段會決定是否包含或排除類別。
為了測試目的,可以透過新增 no-builtin-caller-filter
選項來停用 Java 類別程式庫查詢的內建篩選器,但產生的中繼資料檔案通常不適用於建置。同樣地,也可以使用 no-builtin-heuristic-filter
停用基於啟發式的 Java VM 內部存取的內建篩選器,而且通常也會導致較少可用的中繼資料檔案。例如:-agentlib:native-image-agent=no-builtin-caller-filter,no-builtin-heuristic-filter,config-output-dir=...
存取篩選器 #
與上述基於呼叫端的篩選器不同,基於呼叫端的篩選器會根據動態存取的來源位置篩選動態存取,存取篩選器適用於存取的目標。因此,存取篩選器可以直接排除產生的組態中的套件和類別(及其成員)。
依預設,所有存取的類別(也通過基於呼叫端的篩選器和內建篩選器)都包含在產生的組態中。使用 access-filter-file
選項,可以新增遵循上述檔案結構的自訂篩選器檔案。可以多次指定該選項以新增多個篩選器檔案,並且可以與其他篩選器選項結合使用,例如,-agentlib:access-filter-file=/path/to/access-filter-file,caller-filter-file=/path/to/caller-filter-file,config-output-dir=...
。
將組態檔指定為引數 #
可以使用 -H:ConfigurationFileDirectories=/path/to/config-dir/
將包含不屬於類別路徑的組態檔的目錄指定給 native-image
。此目錄必須直接包含 reachability-metadata.json 或先前使用的個別中繼資料檔案 (jni-config.json、reflect-config.json、proxy-config.json、serialization-config.json 和 resource-config.json)。可以使用 -H:ConfigurationResourceRoots=path/to/resources/
提供位於類別路徑上但不在 META-INF/native-image/
中的相同中繼資料檔案的目錄。-H:ConfigurationFileDirectories
和 -H:ConfigurationResourceRoots
也都可以採用逗號分隔的目錄清單。
透過處理環境注入代理程式 #
如果 Java 程序是由應用程式或腳本檔案啟動,或者 Java 甚至嵌入在現有的程序中,要修改 java
命令列來注入代理程式可能會很困難。在這種情況下,也可以透過 JAVA_TOOL_OPTIONS
環境變數來注入代理程式。這個環境變數可能會被多個同時執行的 Java 程序所讀取,在這種情況下,每個代理程式必須使用 config-output-dir
寫入到不同的輸出目錄。(下一節將描述如何合併多組組態檔。)為了使用單一全域的 JAVA_TOOL_OPTIONS
變數來設定不同的路徑,代理程式的輸出路徑選項支援使用佔位符。
export JAVA_TOOL_OPTIONS="-agentlib:native-image-agent=config-output-dir=/path/to/config-output-dir-{pid}-{datetime}/"
{pid}
佔位符會被替換成程序識別碼,而 {datetime}
會被替換成系統日期和時間(以 UTC 為準),並按照 ISO 8601 格式化。以上述範例來說,產生的路徑可能是:/path/to/config-output-dir-31415-20181231T235950Z/
。
追蹤檔案 #
在上面的範例中,native-image-agent
同時用於追蹤 JVM 上的動態存取,並從中產生一組組態檔。然而,為了更好地了解執行情況,代理程式也可以寫入 JSON 格式的追蹤檔案,其中包含每次單獨的存取。
$JAVA_HOME/bin/java -agentlib:native-image-agent=trace-output=/path/to/trace-file.json ...
native-image-configure
工具可以將追蹤檔案轉換為組態檔。以下命令會讀取並處理 trace-file.json
,並在 /path/to/config-dir/
目錄中產生一組組態檔。
native-image-configure generate --trace-input=/path/to/trace-file.json --output-dir=/path/to/config-dir/
互通性 #
此代理程式使用 JVM 工具介面(JVMTI),並且可能與其他支援 JVMTI 的 JVM 一起使用。在這種情況下,必須提供代理程式的絕對路徑。
/path/to/some/java -agentpath:/path/to/graalvm/jre/lib/amd64/libnative-image-agent.so=<options> ...
實驗性選項 #
此代理程式有一些目前為實驗性的選項,這些選項可能會在未來的版本中啟用,但也可能會被更改或完全移除。請參閱 ExperimentalAgentOptions.md 指南。
Native Image 設定工具 #
如上一節所述,當同時在多個程序中使用代理程式時,config-output-dir
是一個安全的選項,但它會產生多組組態檔。可以使用 native-image-configure
工具來合併這些組態檔。
native-image-configure generate --input-dir=/path/to/config-dir-0/ --input-dir=/path/to/config-dir-1/ --output-dir=/path/to/merged-config-dir/
此命令會從 /path/to/config-dir-0/
讀取一組組態檔,並從 /path/to/config-dir-1/
讀取另一組組態檔,然後將包含它們所有資訊的一組組態檔寫入到 /path/to/merged-config-dir/
。可以指定任意數量的 --input-dir
參數,其中包含組態檔。請參閱 native-image-configure help
以取得所有選項。