◀返回
明確指定類別初始化
預設情況下,Native Image 會在執行階段初始化應用程式類別,但 Native Image 證明「安全」可在建置階段初始化的類別除外。不過,您可以透過明確指定要建置階段或執行階段初始化的類別,來影響預設行為。為此,有兩個命令列選項:--initialize-at-build-time
和 --initialize-at-run-time
。您可以使用這些選項來指定整個套件或個別類別。例如,如果您有類別 p.C1
、p.C2
、…、p.Cn
,您可以透過將以下選項傳遞給 native-image
,指定套件 p
中的所有類別都在建置階段初始化
--initialize-at-build-time=p
如果您只想要在執行階段初始化套件 p
中的類別 C1
,請使用
--initialize-at-run-time=p.C1
您也可以使用來自 Native Image Feature 介面的 RuntimeClassInitialization
類別,以程式設計方式指定類別初始化。
本指南示範如何透過在執行階段(預設行為)執行類別初始化器來建置原生可執行檔,然後在建置階段執行,並比較這兩種方法。
先決條件
請確認您已安裝 GraalVM JDK。最簡單的入門方法是使用 SDKMAN!。如需其他安裝選項,請造訪下載區段。
執行範例
在此範例中,執行一個簡單的 Java 應用程式,該應用程式會剖析 2023 年的一些 Java 演講。剖析器會建立記錄,並將它們新增至 List<Talk>
集合。
- 將以下 Java 原始碼儲存至名為 TalkParser.java 的檔案中
import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class TalkParser { private static final List<Talk> TALKS = new ArrayList<>(); static { Scanner s = new Scanner(""" Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam Anatomy of a Spring Boot App with Clean Architecture by Steve Pember Java in the Cloud with GraalVM by Alina Yurenko Bootiful Spring Boot 3 by Josh Long """); while (s.hasNextLine()) { TALKS.add(new Talk(s.nextLine())); } s.close(); } public static void main(String[] args) { System.out.println("Talks loaded using scanner:"); for (Talk talk : TALKS) { System.out.println("- " + talk.name()); } } } record Talk (String name) {}
- 編譯應用程式
javac TalkParser.java
- 建置原生可執行檔,明確地在執行階段執行類別初始化器
native-image --initialize-at-run-time=TalkParser,Talk -o runtime-parser TalkParser
您可以在此範例中省略
--initialize-at-run-time=TalkParser,Talk
選項,因為這些類別預設會標記為在執行階段初始化。-o
選項指定輸出檔案的名稱。 - 執行和
time
原生應用程式time ./runtime-parser
在具有 16 GB 記憶體和 8 個核心的電腦上,您應該會看到類似以下的結果:``` 使用掃描器載入的演講
- Venkat Subramaniam 的 Java 非同步程式設計:可供選擇的選項
- Steve Pember 的採用乾淨架構的 Spring Boot 應用程式剖析
- Alina Yurenko 的在雲端中使用 GraalVM 的 Java
- Josh Long 的 Bootiful Spring Boot 3 ./runtime-parser 0.00s 使用者 0.00s 系統 52% cpu 0.010 總計 ``` 應用程式會在執行階段剖析文字區塊。
檢查檔案大小,應該約為 13M
du -sh runtime-parser
- 接下來,建置一個原生可執行檔,在建置階段初始化
TalkParser
,並為輸出檔案提供不同的名稱,以與先前的組建區別開來。Talk
記錄也必須明確初始化,因此此類型的物件會持續保留在可執行堆積中。native-image --initialize-at-build-time=TalkParser,Talk -o buildtime-parser TalkParser
如果您的應用程式將其他類型新增至映像堆積,則必須明確標記每個類型(或對應的套件)以進行建置階段初始化。適當的可操作錯誤訊息將引導您完成此流程。
- 執行並
time
第二個可執行檔以進行比較time ./buildtime-parser
這次您應該會看到類似以下的內容
Talks loaded using scanner: - Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam - Anatomy of a Spring Boot App with Clean Architecture by Steve Pember - Java in the Cloud with GraalVM by Alina Yurenko - Bootiful Spring Boot 3 by Josh Long ./buildtime-parser 0.00s user 0.00s system 53% cpu 0.016 total
檢查檔案大小,應該會減少到約 6.4M!
du -sh buildtime-parser
檔案大小變更是因為 Native Image 在建置階段執行靜態初始化器、剖析文字區塊,並僅將
Talk
記錄保存在可執行檔中。因此,當 Native Image 靜態分析應用程式時,大部分的掃描基礎結構不會變成可到達的,因此不會包含在可執行檔中。
另一個用於更精確地分析應用程式的有價值標準是指令數量,可以使用 Linux perf
分析器取得。
例如,對於此範例應用程式,在建置階段類別初始化的情況下,指令數量減少了近 30%(從 1180 萬減少到 860 萬)
perf stat ./runtime-parser
Talks loaded using scanner:
- Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam
(...)
Performance counter stats for './runtime-parser':
(...)
11,323,415 cycles # 3.252 GHz
11,781,338 instructions # 1.04 insn per cycle
2,264,670 branches # 650.307 M/sec
28,583 branch-misses # 1.26% of all branches
(...)
0.003817438 seconds time elapsed
0.000000000 seconds user
0.003878000 seconds sys
perf stat ./buildtime-parser
Talks loaded using scanner:
- Asynchronous Programming in Java: Options to Choose from by Venkat Subramaniam
(...)
Performance counter stats for './buildtime-parser':
(...)
9,534,318 cycles # 3.870 GHz
8,609,249 instructions # 0.90 insn per cycle
1,640,540 branches # 665.818 M/sec
23,490 branch-misses # 1.43% of all branches
(...)
0.003119519 seconds time elapsed
0.001113000 seconds user
0.002226000 seconds sys
這示範了 Native Image 如何將工作從執行階段轉移到建置階段:當類別在建置階段初始化時,會在建置可執行檔時剖析文字區塊,並且僅包含已剖析的物件。這不僅可以使可執行檔的檔案大小變小,而且執行速度更快:當可執行檔執行時,Talk
記錄已經存在,只需要列印即可。
為了確保使用 Native Image 建置的原生可執行檔盡可能與 HotSpot 行為相容,無法在建置階段安全初始化的應用程式類別會在執行階段初始化。您身為使用者,或您使用的架構,必須明確要求某些類別進行建置階段初始化,才能從較小的檔案大小和更快的執行時間中獲益。請包含正確的資料結構,以避免映像大小暴增。我們也建議僅將 --initialize-at-build-time
與單一類別搭配使用。您可能需要新增許多 --initialize-at-build-time
項目。請注意,不正確的建置階段初始化可能會導致在生產環境中應避免的問題,例如功能異常或包含機密資料(例如密碼或加密金鑰)。
結論
本指南示範了如何影響預設的 native-image
類別初始化原則,並將其設定為根據使用案例在建置階段初始化特定類別。建置階段與執行階段初始化的優點會在Native Image 中的類別初始化中說明,但簡而言之,正確使用建置階段初始化可以顯著減少整體檔案大小,並改善應用程式的執行階段。