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

JVM 類加載機制深度解析:七階段生命周期、初始化觸發條件與死鎖案例

     編輯:Java 綜合教學

JVM 類加載機制深度解析

類從加載到卸載的完整生命周期包含七個階段。本文結合 HotSpot 實現、字節碼指令與多線程案例,詳細闡述各階段職責、<clinit> 執行規則、五大主動初始化觸發條件及死鎖風險。

一、七階段生命周期

類加載生命周期
  1. 加載:獲取二進制字節流 → 轉為方法區運行時結構 → 生成 java.lang.Class 對象。
  2. 驗證:文件格式、元數據、字節碼、符號引用四層校驗。可用 -Xverify:none 關閉加速啟動。
  3. 準備:為 static 字段分配方法區內存,賦零值(final static 編譯期常量除外,直接賦值)。
  4. 解析:常量池符號引用 → 直接引用(類/字段/方法/介面方法/方法類型/方法句柄/調用點限定符)。
  5. 初始化:執行 <clinit>(靜態變量賦值 + static{} 塊,按源碼順序)。
  6. 使用:應用程序正常使用類。
  7. 卸載:類加載器被 GC,類元數據從元空間移除。

順序性:前五階段嚴格串行開始,解析可在初始化後(支援動態綁定)。

二、初始化階段核心:<clinit> 方法

  • 由編譯器自動收集靜態賦值與 static{} 塊合成,不需顯式調用父類 <clinit>,JVM 保證父類先執行。
  • 靜態塊只能訪問定義在它之前的靜態變量(前向引用僅限賦值,不可讀取)。
  • 介面亦有 <clinit>,但執行不要求父介面先執行,僅在使用父介面字段時觸發。
  • 線程安全:JVM 內部同步,多線程並發初始化同一類時,僅一線程執行 <clinit>,其餘阻塞等待。

三、死鎖案例:長耗時 <clinit> 阻塞線程

static class DeadLoopClass {
    static {
        if (true) {
            System.out.println(Thread.currentThread() + " init DeadLoopClass");
            while (true) { } // 模擬極長初始化
        }
    }
}

public static void main(String[] args) {
    Runnable r = () -> {
        System.out.println(Thread.currentThread() + " start");
        new DeadLoopClass();
        System.out.println(Thread.currentThread() + " run over");
    };
    new Thread(r).start();
    new Thread(r).start();
}

輸出:

Thread[Thread-0] start
Thread[Thread-1] start
Thread[Thread-0] init DeadLoopClass
// Thread-1 永久阻塞,Thread-0 死循環

若改為 sleep(10s),則 Thread-0 睡眠 10s 後釋放鎖,Thread-1 喚醒直接跳過 <clinit> 繼續執行(同一加載器下類僅初始化一次)。

四、五大主動初始化觸發條件(JDK 1.7+)

  1. 字節碼指令:new、getstatic、putstatic、invokestatic。對應場景:實例化、讀寫非常量靜態字段、調用靜態方法。
  2. 反射調用:Class.forName、Method.invoke、Constructor.newInstance 等。
  3. 父類初始化:子類初始化前,父類必須已初始化。
  4. 主類啟動:JVM 啟動執行 main() 所在類。
  5. 動態語言支援:MethodHandle 解析結果為 REF_getStatic/REF_putStatic/REF_invokeStatic 且類未初始化。

五、被動引用不觸發初始化(三大典型場景)

  1. 子類引用父類靜態字段:System.out.println(SubClass.value) 僅觸發父類初始化。
  2. 數組定義:SuperClass[] arr = new SuperClass[10] 不初始化 SuperClass(創建的是數組類)。
  3. 常量折疊:final static 基本類型/字符串常量編譯期進入調用類常量池,不觸發定義類初始化。

六、實戰啟發

  • 避免在 static{} 執行耗時操作(IO、網絡、鎖),防止類初始化死鎖或啟動緩慢。
  • 利用「類初始化單次性」實現線程安全單例(static final Instance INSTANCE = new Instance())。
  • 動態代理/字節碼生成框架(CGLIB、ByteBuddy)頻繁觸發類加載,需關注元空間 OOM 風險。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved