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

JVM 核心機制深度解析:DLL 載入流程與類載入體系全景圖

     編輯:Java 相關知識

前言

Java 程式從啟動到執行,經歷了「JVM DLL 載入 → JNI 環境建立 → 類載入器層級委派 → 字節碼驗證/準備/解析/初始化」的完整鏈路。理解這兩大核心流程,不僅有助於排查 UnsatisfiedLinkError、NoClassDefFoundError 等疑難雜症,更是掌握熱部署、模組化、GraalVM 原生編譯等進階技術的基石。本文將從原始碼視角,拆解 HotSpot JVM 在 Windows 平台的啟動載入與類載入全過程。

一、JVM DLL 載入流程(Windows 平台)

1.1 啟動入口:java.exe 的四大步驟

  1. 建立 JVM 載入環境與配置:解析命令列參數、環境變數。
  2. 載入 jvm.dll:透過 LoadLibrary 載入動態連結庫。
  3. 初始化 JVM 並綁定 JNIEnv:呼叫 JNI_CreateJavaVM 取得 JNIEnv* 介面指標。
  4. 載入主類並啟動:透過 JNIEnv->FindClass 載入含 main 方法的類,呼叫 CallStaticVoidMethod 執行。

1.2 關鍵路徑解析:JRE_HOME 怎麼找到的?

java.exe 透過 GetApplicationHome(java_md.c)取得自身路徑,依序嘗試:

  1. <java.exe目錄>/../jre/bin/java.dll 存在 → <java.exe目錄>/.. 為 JRE_HOME
  2. <java.exe目錄>/../jre/bin/java.dll 存在 → <java.exe目錄>/../jre 為 JRE_HOME
  3. 讀取註冊表 HKLM\Software\JavaSoft\Java Runtime Environment\<版本>\JavaHome

可用 System.getProperty("java.home") 驗證結果。

1.3 JVM.cfg:多 JVM 版本的選擇策略

JRE_HOME/lib/<arch>/jvm.cfg 定義支援的 JVM 類型:

-client KNOWN
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
  • KNOWN:該 JVM 存在
  • ALIASED_TO:別名指向
  • WARN:缺失時降級替換
  • ERROR:缺失拋異常

啟動時可透過 -client、-server、-XXaltJVM=<path> 指定;未指定則取 cfg 第一個 KNOWN 項。設定 _JAVA_LAUNCHER_DEBUG=1 可在主控台列印解析細節。

1.4 JNI 綁定與主類啟動

  • LoadLibrary 載入 jvm.dll,取得 JNI_CreateJavaVM、JNI_GetDefaultJavaVMInitArgs 函數指標。
  • 呼叫 JNI_CreateJavaVM 獲得 JNIEnv 實例。
  • 若為 -jar:解析 META-INF/MANIFEST.MF 的 Main-Class;否則直接用命令列類名。
  • FindClass 載入主類 → GetStaticMethodID 找 main → CallStaticVoidMethod 執行。

二、JVM 類載入體系:雙親委派與模組化演進

2.1 三階段七步驟標準流程

  1. 載入:尋找並導入二進制資料 → 生成 Class 物件
  2. 連結(可分步、解析可選)
    • 驗證:檢查字節碼合法性
    • 準備:靜態欄位分配記憶體、賦預設值
    • 解析:符號引用 → 直接引用
  3. 初始化:執行 <clinit>(靜態變數賦值、靜態區塊)

2.2 ClassLoader 核心方法與自定義實現

自定義 ClassLoader 範本

public class MultiClassLoader extends ClassLoader {
    private Map<String, Class<?>> cache = new HashMap<>();

    @Override
    public synchronized Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        // 1. 快取檢查
        Class<?> clazz = cache.get(name);
        if (clazz != null) return clazz;

        // 2. 委派父載入器(系統載入器)
        try {
            clazz = super.findSystemClass(name);
            return clazz;
        } catch (ClassNotFoundException ignored) {}

        // 3. 自定義載入邏輯(網路、加密、動態生成等)
        byte[] bytes = loadClassBytes(name); // 子類實現
        if (bytes == null) throw new ClassNotFoundException();

        clazz = defineClass(null, bytes, 0, bytes.length);
        if (resolve) resolveClass(clazz);

        cache.put(name, clazz);
        return clazz;
    }

    protected abstract byte[] loadClassBytes(String name);
}

2.3 雙親委派模型與載入器層級

Bootstrap ClassLoader (C++ 實現,null 代表)
    ↑
Extension ClassLoader (Java 實現,載入 jre/lib/ext)
    ↑
Application ClassLoader (載入 CLASSPATH,用戶自定義載入器預設父)
    ↑
自定義 ClassLoader (Applet、Tomcat WebappClassLoader、OSGi 模組載入器...)
  • 委派規則:子載入器收到請求 → 先委派父載入器 → 父無法載入 → 子自行載入。
  • 隔離性:同名類由不同載入器載入視為不同類型(ClassCastException 根因)。
  • 可見性:子載入器可見父載入器類,父不可見子載入器類。

2.4 實戰案例:Tomcat 打破雙親委派實現 Web 應用隔離

Tomcat WebappClassLoader 先嘗試自行載入 Webapp 目錄類(WEB-INF/classes、WEB-INF/lib),失敗再委派父載入器。確保各 Webapp 可使用不同版本同名庫(如 Log4j 1.x vs 2.x),互不干擾。

三、模組化時代(Java 9+)的類載入變革

  • 模組系統:module-info.java 宣告依賴、導出封裝,載入器按模組圖解析,強封裝內部 API。
  • 層級變化:Bootstrap → Platform → Application,Extension 機制移除。
  • 非標準 API 遷移:--add-exports、--add-opens 替代舊有破壞封裝手段。

四、總結與排查清單

方法用途
loadClass(name, resolve)入口點,雙親委派邏輯核心
defineClass(bytes)字節碼 → Class 物件(解析、驗證)
findSystemClass(name)從本地檔案系統載入(Bootstrap 預設機制)
resolveClass(Class)觸發解析階段
findLoadedClass(name)檢查是否已載入,避免重複
現象可能原因排查方向
UnsatisfiedLinkErrorjvm.dll 路徑錯誤、架構不匹配(x86/x64)、原生庫缺失檢查 java.library.path、JRE_HOME 解析、_JAVA_LAUNCHER_DEBUG
NoClassDefFoundError類初始化失敗、載入器委派鏈斷裂、模組未導出-verbose:class 追蹤載入順序、檢查 module-info
ClassCastException(同名類)雙親委派被破壞、多載入器重複載入確認載入器層級、ClassLoader.getClassLoader() 比對

掌握 JVM DLL 載入與類載入雙主線,能讓你從「會用 Java」進階到「懂 Java 運作本質」。建議收藏排查清單,下次遇到載入異常不再無從下手。

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