首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

JVM 類加載機制全解析:生命週期、雙親委派與熱部署原理

     編輯:Java 綜合教學

類的生命週期:七大階段

類生命週期圖

階段順序:加載 → 驗證 → 準備 → 解析 → 初始化 → 使用 → 卸載。注意:階段啟動順序固定,但執行可交叉(如驗證與準備可並行)。

類初始化的五種強制觸發場景(JVM 規範)

  1. new 實例化、讀寫靜態欄位(非 final 常數)、呼叫靜態方法
  2. 反射呼叫(Class.forName、Method.invoke)
  3. 初始化子類時,父類未初始化則先初始化父類
  4. JVM 啟動指定主類(含 main 方法)
  5. JDK 1.7+ 動態語言支援:MethodHandle 解析為靜態欄位/方法句柄且類未初始化

⚠️ final static 基本型別/字串常數編譯期進入常數池,不觸發類初始化。

加載階段三大任務

  1. 透過全限定名獲取二進制位元組流
  2. 將靜態儲存結構轉為方法區執行時資料結構
  3. 在堆中生成 java.lang.Class 物件,作為方法區資料的存取入口

驗證階段:四層防線

  1. 檔案格式驗證:魔數 0xCAFEBABE、版本號、常數池合法性
  2. 元資料驗證:語義分析(是否有父類、是否繼承 final 類、抽象方法實作、欄位/方法衝突)
  3. 位元組碼驗證:資料流/控制流分析,確保運算元棧與指令序列匹配(如防止 int 傳給 long 指令)
  4. 符號引用驗證:解析階段進行,確認類/欄位/方法存在且存取權限合法

存取權限範圍圖

準備階段:靜態欄位記憶體分配與預設值

僅針對 static 變數(不含實例變數),在方法區分配記憶體並設為零值(非程式碼指定的初始值)。

型別零值
int/short/byte0
long0L
float/double0.0f/0.0d
char'\u0000'
booleanfalse
referencenull

解析階段:符號引用 → 直接引用

  • 符號引用:以字面量描述目標(如類全名、方法描述符),目標未必已載入
  • 直接引用:指標、偏移量或句柄,目標必已在記憶體中

針對 7 類符號引用:類/介面、欄位、類方法、介面方法、方法型別、方法句柄、呼叫點限定符。

初始化階段:執行 <clinit>() 與建構子

  • 依源碼順序執行靜態變數賦值與 static {} 區塊
  • 靜態區塊只能存取定義在它之前的靜態變數(賦值可、讀取不可)
  • 父類 <clinit>() 先於子類執行;介面無 static {},僅在使用介面常數時初始化父介面
  • 多執行緒並發初始化同一類時,JVM 保證僅一執行緒執行 <clinit>(),其餘阻塞等待
// 執行順序示例:B1 → A1 → B2 → A2 → B2 → A2
class Parent { static { System.out.println("B1"); } Parent() { System.out.println("B2"); } }
class Child extends Parent { static { System.out.println("A1"); } Child() { System.out.println("A2"); } }

類加載器與雙親委派模型

類加載器層級圖

加載器搜尋範圍
啟動類加載器JAVA_HOME/lib、-Xbootclasspath 指定路徑
擴充類加載器JAVA_HOME/lib/ext、java.ext.dirs
應用程式類加載器ClassPath (用戶類別)
自定義類加載器繼承 ClassLoader,覆寫 findClass

雙親委派機制

  1. 收到載入請求 → 委派給父加載器
  2. 層層向上至啟動類加載器
  3. 父加載器無法載入(搜尋範圍無該類)→ 子加載器嘗試載入

優勢:避免重複載入、保護核心類庫不被竄改(如無法自定義 java.lang.String)、確保類在 JVM 中唯一性(同一類加載器下)。

自定義類加載器與熱部署原理

public class HotSwapClassLoader extends ClassLoader {
    private String classPath;
    @Override protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = loadClassBytes(name); // 從檔案/網路/資料庫讀取位元組碼
        return defineClass(name, data, 0, data.length);
    }
}

熱部署核心:棄用舊 ClassLoader、建立新 ClassLoader 重新載入更新後的類。舊實例持有舊 Class 參考,新實例使用新 Class,實現無重啟更新。注意:需處理靜態狀態遷移、資源釋放、執行緒安全等問題。

⚠️ 破壞雙親委派:覆寫 loadClass;遵守規範:覆寫 findClass。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved