原生映像中的類別初始化
Java 的語意要求類別在執行時期第一次被存取時進行初始化。類別初始化對於提前編譯 Java 應用程式會產生負面影響,原因如下:
- 它會顯著降低原生可執行檔的效能:每次存取類別(透過欄位或方法)都需要檢查以確保該類別已經初始化。如果沒有最佳化,這會使效能降低兩倍以上。
- 它增加了啟動應用程式所需的計算量和時間。例如,簡單的「Hello, World!」應用程式需要初始化 300 多個類別。
為了減少類別初始化的負面影響,原生映像支援在建置時進行類別初始化:它可以在建置可執行檔時初始化類別,使執行時初始化和檢查變得不必要。所有已初始化類別的靜態狀態都儲存在可執行檔中。存取在建置時初始化的類別的靜態欄位對於應用程式來說是透明的,並且運作方式就像該類別在執行時初始化一樣。
但是,Java 類別初始化語意施加了一些限制,使類別初始化策略複雜化,例如:
- 當初始化類別時,其所有超類別和具有預設方法的超介面也必須初始化。但是,沒有預設方法的介面則不會初始化。為了滿足此要求,使用短期「相關超類型」,以及用於具有預設方法的類別和介面的子類型的「相關子類型」。
- 在建置時初始化的類型的相關超類型也必須在建置時初始化。
- 在執行時初始化的類型的相關子類型也必須在執行時初始化。
- 可執行檔中不得存在在執行時初始化的類別的實例。
為了享有原生映像的完整開箱即用體驗,並仍然獲得建置時初始化的好處,原生映像會執行兩項操作:
若要追蹤哪些類別已初始化以及原因,請將命令列選項 -H:+PrintClassInitialization
傳遞給 native-image
工具。此選項可協助您設定 native image
建置器以根據需要運作。目標是在建置時初始化盡可能多的類別,同時保持應用程式的正確語意。
建置時初始化 #
原生映像會在建置時初始化大多數 JDK 類別,包括垃圾收集器、重要的 JDK 類別和去最佳化器。對於所有在建置時初始化的類別,原生映像都會提供適當的支援,以便儘管類別初始化在建置時發生,語意仍保持一致。如果您發現因在建置時進行類別初始化而導致 JDK 類別行為不正確的問題,請回報問題。
自動初始化安全類別 #
對於應用程式類別,原生映像會嘗試尋找可以在建置時安全初始化的類別。如果類別的所有相關超類型都是安全的,並且類別初始化程式沒有呼叫任何不安全的方法或初始化其他不安全的類別,則該類別被視為安全。
如果方法符合下列條件,則該方法被視為不安全:
- 它會過渡性地呼叫原生程式碼 (例如
System.out.println
):原生程式碼未經過分析,因此原生映像無法知道是否執行了非法動作。 - 它呼叫無法縮減為單一目標的方法(虛擬方法)。此限制避免了靜態初始化程式的安全分析的搜尋空間爆炸。
- 它被原生映像取代。執行被取代方法的初始化程式會在託管 Java 虛擬機 (JVM) 中產生與產生的可執行檔不同的結果。因此,安全分析會將某些方法視為安全,但呼叫它們會導致非法狀態。
透過 -H:+PrintClassInitialization
命令列選項,所有經證明安全的類別清單會輸出到 native-image
工具。
注意:您也可以明確指定類別初始化。