jvm類載入機制
Java 虛擬機器將位元組流轉化為 Java 類的過程。這個過程可分為載入、連結以及初始化
三大步驟。
- 載入是指查詢位元組流,並且據此建立類的過程。載入需要藉助類載入器,在 Java 虛擬機器中,類載入
器使用了雙親委派模型,即接收到載入請求時,會先將請求轉發給父類載入器。
- 連結,是指將建立成的類合併至 Java 虛擬機器中,使之能夠執行的過程。連結還分驗證、準備和解析
三個階段。其中,解析階段為非必須的。
- 初始化,則是為標記為常量值的欄位賦值,以及執行 < clinit > 方法的過程。類的初始化僅會被執
行一次,這個特性被用來實現單例的延遲初始化。
載入
載入,是指查詢位元組流,並且據此建立類的過程。前面提到,對於陣列類來說,它並沒有對應的字
節流,而是由 Java 虛擬機器直接生成的。對於其他的類來說,Java 虛擬機器則需要藉助類載入器來完
成查詢位元組流的過程。
由類載入器classLoadder負責類裝載,JDK 預設提供瞭如下幾種ClassLoader
- Bootstrp loader
Bootstrp載入器是用C++語言寫的,它是在Java虛擬機器啟動後初始化的,它主要負責載入%JAVA_HOME%/jre/lib,-Xbootclasspath引數指定的路徑以及%JAVA_HOME%/jre/classes中的類。
- ExtClassLoader
Bootstrp loader載入ExtClassLoader,並且將ExtClassLoader的父載入器設定為Bootstrp loader.ExtClassLoader是用Java寫的,具體來說就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要載入%JAVA_HOME%/jre/lib/ext,此路徑下的所有classes目錄以及java.ext.dirs系統變數指定的路徑中類庫。
- AppClassLoader
Bootstrp loader載入完ExtClassLoader後,就會載入AppClassLoader,並且將AppClassLoader的父載入器指定為 ExtClassLoader。AppClassLoader也是用Java寫成的,它的實現類是 sun.misc.Launcher$AppClassLoader,另外我們知道ClassLoader中有個getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要負責載入classpath所指定的位置的類或者是jar文件,它也是Java程式預設的類載入器。
java採用了委託模型機制,這個機制簡單來講,就是“類裝載器有載入類的需求時,會先請示其Parent使用其搜尋路徑幫忙載入,如果Parent 找不到,那麼才由自己依照自己的搜尋路徑搜尋類”,或者叫雙親委派模型。
連結
連結,是指將建立成的類合併至 Java 虛擬機器中,使之能夠執行的過程。它可分為驗證、準備以及解
析三個階段。
- 驗證階段的目的,在於確保被載入類能夠滿足 Java 虛擬機器的約束條件。
- 準備階段的目的,則是為被載入類的靜態欄位分配記憶體。Java 程式碼中對靜態欄位的具體初始化,則
會在稍後的初始化階段中進行。
在 class 檔案被載入至 Java 虛擬機器之前,這個類無法知道其他類及其方法、欄位所對應的具體地
址,甚至不知道自己方法、欄位的地址。因此,每當需要引用這些成員時,Java 編譯器會生成一個
符號引用。在執行階段,這個符號引用一般都能夠無歧義地定位到具體目標上。
初始化
在 Java 程式碼中,如果要初始化一個靜態欄位,我們可以在宣告時直接賦值,也可以在靜態程式碼塊中
對其賦值。
類的初始化何時會被觸發呢?JVM 規範枚舉了下述多種觸發情況:
- 當虛擬機器啟動時,初始化使用者指定的主類;
- 當遇到用以新建目標類例項的 new 指令時,初始化 new 指令的目標類;
- 當遇到呼叫靜態方法的指令時,初始化該靜態方法所在的類;
- 當遇到訪問靜態欄位的指令時,初始化該靜態欄位所在的類;
- 子類的初始化會觸發父類的初始化;
- 如果一個介面定義了 default 方法,那麼直接實現或者間接實現該介面的類的初始化,會觸發
該介面的初始化;
- 使用反射 API 對某個類進行反射呼叫時,初始化這個類;
- 當初次呼叫 MethodHandle 例項時,初始化該 MethodHandle 指向的方法所在的類。