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

java.lang.Class 對象。-Xverify:none 關閉加速啟動。static 字段分配方法區內存,賦零值(final static 編譯期常量除外,直接賦值)。<clinit>(靜態變量賦值 + static{} 塊,按源碼順序)。順序性:前五階段嚴格串行開始,解析可在初始化後(支援動態綁定)。
<clinit> 方法static{} 塊合成,不需顯式調用父類 <clinit>,JVM 保證父類先執行。<clinit>,但執行不要求父介面先執行,僅在使用父介面字段時觸發。<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> 繼續執行(同一加載器下類僅初始化一次)。
new、getstatic、putstatic、invokestatic。對應場景:實例化、讀寫非常量靜態字段、調用靜態方法。Class.forName、Method.invoke、Constructor.newInstance 等。main() 所在類。MethodHandle 解析結果為 REF_getStatic/REF_putStatic/REF_invokeStatic 且類未初始化。System.out.println(SubClass.value) 僅觸發父類初始化。SuperClass[] arr = new SuperClass[10] 不初始化 SuperClass(創建的是數組類)。final static 基本類型/字符串常量編譯期進入調用類常量池,不觸發定義類初始化。static{} 執行耗時操作(IO、網絡、鎖),防止類初始化死鎖或啟動緩慢。static final Instance INSTANCE = new Instance())。