1. 程式人生 > >深入理解JVM(③)虛擬機器的類載入時機

深入理解JVM(③)虛擬機器的類載入時機

## 前言 Java虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這個過程被稱為虛擬機器的類載入機制。 ### 類載入的時機 一個型別從被載入到虛擬機器記憶體中開始,到解除安裝除記憶體為止,它的生命週期將會經歷==載入(Loading)==、==驗證(Verification)==、==準備(Preparation)==、==解析(Resolution)==、==初始化(Initialization)==、==使用(Using)和 解除安裝(Unloading)==、七個階段,其中驗證、準備、解析三個部分統稱為==連線(Linking)==。 類的生命週期如下圖: ![類的生命週期](https://img-blog.csdnimg.cn/20200624165700383.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_SmlNb2Vy,size_60,color_c8cae6,t_90) 其實載入、驗證、準備、初始化和解除安裝這五個階段的順序是確定的,型別的載入過程必須按照這種順序按部就班地開始,而解析階段則不一定:==它在某些情況下可以在初始化階段之後再開始,這是為了支援Java語音的執行時繫結特性(也稱為動態繫結或晚期繫結)==。 在什麼情況下需要開始類載入過程的第一個階段“**載入**”,《Java虛擬機器規則》中並沒有進行強制約束,但是對於初始化階段《Java虛擬機器規範》則是嚴格規定了有且只有以下六種情況必須立即對類進行“**==初始化==**”。 1. ==遇到`new`、`getstatic`、`putstatic`或`invokestatic`這四條位元組碼指令時,如果型別沒有進行過初始化,則需要先觸發其初始化階段==。 涉及到這四條指令的典型場景有: - 使用new關鍵字例項化對的時候。 - 讀取或設定一個型別的靜態欄位(被final修飾、已在編譯期把結果放入常量池的靜態欄位除外)的時候。 - 呼叫一個型別的靜態方法的時候。 2. ==使用 `java.lang.reflect` 包的方法對型別進行反射呼叫的時候,如果型別沒有進行過初始化,則需要先觸發其初始化。== 3. ==當初始化型別的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。== 4. ==當虛擬機器啟動時,使用者需要指定一個要執行的主類(包含main()方法的那個類),虛擬機器會先初始化這個主類。== 5. ==當使用JDK7新加入的動態語言支援時,如果一個`java.lang.invoke.MethodHandle`例項最後的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四種類型的方法控制代碼,並且這個方法控制代碼對應的類沒有進行過初始化,則需要先觸發其初始化。== 6. 當一個介面中定義了JDK8新加入的預設方法(被`default`關鍵字修飾的介面方法)時,如果這個介面的實現類發生了初始化,那該介面要在其之前被初始化。 除了以上的這個六種場景外,所有引用型別的方式都不會觸發初始化,稱為被動引用。 下面來看一下哪些是被動引用: #### 例子