天天看點

徹底弄懂類加載和JVM的雙親委派機制

類加載

類的生命周期會經曆以下 7 個階段:

  1. 加載階段(Loading)
  2. 驗證階段(Verification)
  3. 準備階段(Preparation)
  4. 解析階段(Resolution)
  5. 初始化階段(Initialization)
  6. 使用階段(Using)
  7. 解除安裝階段(Unloading)

其中驗證、準備、解析 3 個階段統稱為連接配接(Linking),如下圖所示

徹底弄懂類加載和JVM的雙親委派機制

JVM 類加載通常指的就是前五個階段:加載、驗證、準備、解析、初始化

1.加載階段

此階段用于查到相應的類(通過類名進行查找)并将此類的位元組流轉換為方法區運作時的資料結構,然後再在記憶體中生成一個能代表此類的 java.lang.Class 對象,作為其他資料通路的入口。

2.驗證階段

此步驟主要是為了驗證位元組碼的安全性,如果不做安全校驗的話可能會載入非安全或有錯誤的位元組碼,進而導緻系統崩潰,它是 JVM 自我保護的一項重要舉措。

驗證的主要動作大概有以下幾個:

  • 檔案格式校驗包括常量池中的常量類型、Class 檔案的各個部分是否被删除或被追加了其他資訊等;
  • 中繼資料校驗包括父類正确性校驗(檢查父類是否有被 final 修飾)、抽象類校驗等;
  • 位元組碼校驗,此步驟最為關鍵和複雜,主要用于校驗程式中的語義是否合法且符合邏輯;
  • 符号引用校驗,對類自身以外比如常量池中的各種符号引用的資訊進行比對性校驗。

3.準備階段

此階段是用來初始化并為類中定義的靜态變量配置設定記憶體的,這些靜态變量會被配置設定到方法區上。

HotSpot 虛拟機在 JDK 1.7 之前都在方法區,而 JDK 1.8 之後此變量會随着類對象一起存放到 Java 堆中。

4.解析階段

此階段主要是用來解析類、接口、字段及方法的,解析時會把符号引用替換成直接引用。

所謂的符号引用是指以一組符号來描述所引用的目标,符号可以是任何形式的字面量,隻要使用時能無歧義地定位到目标即可;而直接引用是可以直接指向目标的指針、相對偏移量或者是一個能間接定位到目标的句柄。

符号引用和直接引用有一個重要的差別:使用符号引用時被引用的目标不一定已經加載到記憶體中;而使用直接引用時,引用的目标必定已經存在虛拟機的記憶體中了。

5.初始化

初始化階段 JVM 就正式開始執行類中編寫的 Java 業務代碼了。到這一步驟之後,類的加載過程就算正式完成了。

JVM的雙親委派機制

Java類加載并非單純按照類逐個進行加載,我們知道有通常有多個父類和多個子類,面對複雜的類,那如何保證類加載的有序性和安全性呢?

這就是本文要講到的重點JVM的雙親委派機制。

在講雙親委派機制之前,我們需要先了解一下JVM類加載器。

JVM類加載器有三種:

  1. 啟動類加載器
  2. 擴充類加載器
  3. 應用程式類加載器

除以上三種外,我們還可以自定義類加載器。

啟動類加載器

負責加載Java_HOME目錄下的lib中的類庫,是jdk的核心類庫,同時我們也可以通過-Xbootclasspath參數指定路徑中被虛拟機認可的類庫。

擴充類加載器

負責加載Java_HOME中lib子下ext子目錄中的類庫,也就是加載JDK的擴充類庫,同時我們可以通過java.ext.dirs系統參數變量加載指定路徑中的類庫。

應用程式類加載器

負責加載使用者路徑classpath上的類庫

另外, 我們也可以通過繼承java.lang.ClassLoader,根據不同需求實作自定義類的加載器。

向上委派機制

向上委派機制指一個類在收到類加載請求後, 不會嘗試自己加載這個類,而是把這個類加載請求向上委派給他的父類去完成,父類在接收到這個類加載請求後,又委派給他的父類,依次類推,這樣的話,所有的類加載請求都被向上委派到啟動類加載器中,這樣的委派機制叫做向上委派機制。

向上委派的過程分為下圖中3步

徹底弄懂類加載和JVM的雙親委派機制

向下委派機制

當父類加載器接收到類加載請求後,發現自己也無法加載這個類,出現這種情況,通常是這個類的Class檔案在父類的類加載路徑中不存在,這時,父類會将這個資訊回報給子類,并向下委派子類加載器加載這個類,知道他被成功加載,如果這時仍然找不到這個類,那麼JVM就會抛出ClassNotFoud異常。

向下委派類加載過程,分為下圖中的四步

徹底弄懂類加載和JVM的雙親委派機制

如果最終在自定義類路徑下仍找不到目标Class檔案,那麼JVM就會抛出ClassNotFoud異常,類加載過程失敗,JVM也會啟動失敗。

雙親委派中向上委派保證了先加載JDK的核心類,再加載應用程式的類,有效防止了某個應用程式中因為某個類存在的一些不安全問題而導緻JVM變得不安全,而從上向下的委派過程則保障了需要加載的類,都得到了加載。如果從上到下仍然在自定義類路徑下仍找不到目标Class檔案,那麼JVM就會抛出ClassNotFoud異常,類加載過程失敗,JVM也會啟動失敗。如果類名和包名一樣也無法完成類加載。

徹底弄懂類加載和JVM的雙親委派機制

聲明:本文為極客時間每日一課學習做的筆記。