原生映像檔相容性指南
原生映像檔使用與傳統 Java 虛擬機器 (VM) 不同的方式編譯 Java 應用程式。它區分建置時間和執行時間。在映像檔建置時間,native-image
建置器會執行靜態分析,以找出應用程式進入點可到達的所有方法。然後,建置器會將這些(而且只有這些)方法編譯成可執行二進位檔。由於這種不同的編譯模型,Java 應用程式在編譯成原生映像檔時的行為可能會有所不同。
原生映像檔提供一種最佳化,可減少應用程式的記憶體佔用量和啟動時間。此方法依賴於「封閉世界假設」,其中所有程式碼在建置時都是已知的。也就是說,在執行時間不會載入新的程式碼。與大多數最佳化一樣,並非所有應用程式都適用於此方法。如果 native-image
建置器無法在建置時間最佳化應用程式,它會產生所謂的「回退檔案」,該檔案需要 Java VM 才能執行。我們建議您查看原生映像檔基礎知識,以詳細了解您的 Java 應用程式在建置和執行時間會發生什麼。
需要中繼資料的功能 #
為了適用於封閉世界假設,以下 Java 功能通常需要在建置時間傳遞中繼資料給 native-image
。此中繼資料可確保原生映像檔使用最少的必要空間。
最近,透過發佈 GitHub 上共用的可達性中繼資料,增強了原生映像檔與最熱門 Java 程式庫的相容性。使用者可以分攤維護第三方相依性中繼資料的負擔,並重複使用它。請參閱可達性中繼資料以了解更多資訊。
與封閉世界假設不相容的功能 #
某些 Java 功能在封閉世界假設中尚不受支援,如果使用,則會導致回退檔案。
invokedynamic
位元組碼和方法句柄 #
在封閉世界假設下,必須知道所有呼叫的方法及其呼叫點。invokedynamic
方法和方法句柄可以在執行時間引入呼叫或變更呼叫的方法。
請注意,javac
針對例如 Java Lambda 運算式和字串串連所產生的 invokedynamic
用例是受支援的,因為它們不會在執行時間變更呼叫的方法。
在原生映像檔中可能以不同方式運作的功能 #
原生映像檔實作某些 Java 功能的方式與 Java VM 不同。
安全性管理員 #
即使透過啟動時的 -Djava.security.manager
設定安全性管理員,java.lang.System#getSecurityManager()
永遠都會傳回 null
。
如果 -Djava.security.manager
在程式啟動時設定為任何值(除了 disallow
),則使用非 null 引數呼叫 java.lang.System#setSecurityManager(SecurityManager)
會擲回 java.lang.SecurityException
。
訊號處理常式 #
註冊訊號處理常式需要啟動一個新執行緒來處理訊號並叫用關機掛勾。依預設,在建置原生映像檔時不會註冊任何訊號處理常式,除非使用者明確註冊。例如,不建議在建置共用程式庫時註冊預設訊號處理常式,但在建置用於容器化環境(例如 Docker 容器)的原生可執行檔時,最好包含訊號處理常式。
若要註冊預設訊號處理常式,請將 --install-exit-handlers
選項傳遞給 native-image
建置器。此選項會提供與 Java VM 相同的訊號處理常式。
類別初始化程式 #
依預設,類別會在執行時間初始化。這可確保相容性,但會限制某些最佳化。為了更快啟動和獲得更好的峰值效能,最好在建置時間初始化類別。可以使用選項 --initialize-at-build-time
或 --initialize-at-run-time
為特定類別和套件或所有類別指定類別初始化行為。屬於 JDK 類別程式庫成員的類別依預設會初始化。
注意:在建置時間初始化類別可能會破壞現有程式碼中的特定假設。例如,在類別初始化程式中載入的檔案在建置時可能與執行時的位置不同。此外,某些物件(例如檔案描述子或執行中的執行緒)不得儲存在原生可執行檔中。如果此類物件在建置時間可達,則 native image
建置器會失敗並顯示錯誤。
如需更多資訊,請參閱原生映像檔中的類別初始化。
終結器 #
Java 基底類別 java.lang.Object
定義了方法 finalize()
。當垃圾收集確定沒有更多對物件的參考時,垃圾收集器會在物件上呼叫此方法。子類別可以覆寫 finalize()
方法以處置系統資源或執行其他清除作業。
自 Java SE 9 以來,終結器已被棄用。它們很難實作,並且具有設計不良的語意。例如,終結器可能會透過將其參考儲存在靜態欄位中,導致物件再次可達。因此,不會叫用終結器。我們建議您將終結器取代為弱參考和參考佇列。
執行緒 #
原生映像檔不會實作 java.lang.Thread
中已棄用的方法,例如 Thread.stop()
。
不安全的記憶體存取 #
如果類別在建置時間初始化,則使用 sun.misc.Unsafe
存取的欄位需要標記為這樣,以進行靜態分析。在大多數情況下,這會自動發生:儲存在 static final
欄位中的欄位偏移會自動從託管值(執行 native image
建置器的 Java VM 的欄位偏移)重寫為原生可執行值,並且作為該重寫的一部分,該欄位會標記為 Unsafe
存取。對於非標準模式,可以使用註釋 RecomputeFieldValue
手動重新計算欄位偏移。
除錯與監控 #
Java 有一些可選規格,Java 實作可以使用這些規格來除錯和監控 Java 程式,包括 JVMTI。它們可協助您在執行時間監控 Java VM 的事件,例如編譯,而這在大多數原生映像檔中不會發生。這些介面建立在執行時間可以使用 Java 位元組碼的假設上,而對於使用封閉世界最佳化建置的原生映像檔而言,情況並非如此。由於 native-image
建置器會產生原生可執行檔,因此使用者必須使用原生除錯器和監控工具(例如 GDB 或 VTune),而不是針對 Java 的工具。原生映像檔不支援 JVMTI 和其他基於位元組碼的工具。
Linux AArch64 架構的限制
大多數原生映像檔功能在 Linux AArch64 架構上都受支援,但以下描述的限制除外。
-R:[+|-]WriteableCodeCache
:必須停用。--libc=<value>
:不支援musl
。--gc=<value>
:不支援 G1 垃圾收集器 (G1
)。
請在此處尋找 native-image
建置器的選項清單。