最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JVM類加載機制與雙親委派使用及說明

 更新時間:2026年02月06日 15:27:40   作者:是碼龍不是碼農(nóng)  
文章介紹了JVM的四大類加載器(啟動類加載器、擴展類加載器、應(yīng)用程序類加載器和自定義類加載器),雙親委派機制及其核心優(yōu)勢,以及如何打破雙親委派機制的場景和實現(xiàn)方式

一、JVM 四大類加載器

JVM 的類加載器(ClassLoader)負責(zé)將.class字節(jié)碼文件加載到內(nèi)存中,并生成對應(yīng)的Class對象。

Java 默認提供了四層層級結(jié)構(gòu)的類加載器,從上到下依次為:啟動類加載器、擴展類加載器、應(yīng)用程序類加載器、自定義類加載器。

1. 層級結(jié)構(gòu)與核心特性

類加載器名稱英文 / 簡稱加載范圍實現(xiàn)方式父加載器
啟動類加載器Bootstrap ClassLoaderJRE 核心類庫:rt.jar、resources.jar、sun.boot.class.path路徑下的類(如java.lang.*、java.util.*)C/C++ 實現(xiàn),JVM 內(nèi)置,無對應(yīng) Java 對象,無法在代碼中直接獲取無(頂層加載器)

擴展類加載器

(jdk9+后改名為平臺類加載器)

Extension ClassLoaderJRE 擴展目錄jre/lib/ext、java.ext.dirs指定路徑下的擴展類Java 實現(xiàn),繼承自URLClassLoader啟動類加載器
應(yīng)用程序類加載器Application ClassLoader(系統(tǒng)類加載器)項目classpath下的自定義類、第三方依賴包Java 實現(xiàn),繼承自URLClassLoader,是ClassLoader.getSystemClassLoader()的返回值擴展類加載器
自定義類加載器Custom ClassLoader自定義路徑的類(網(wǎng)絡(luò)、加密字節(jié)碼、自定義目錄等)繼承ClassLoader重寫核心方法實現(xiàn)默認為應(yīng)用程序類加載器

補充關(guān)鍵說明

  • 層級關(guān)系≠繼承關(guān)系:類加載器的父子關(guān)系是組合引用(通過parent字段關(guān)聯(lián)),不是 Java 類的繼承;
  • 獲取系統(tǒng)類加載器:代碼中調(diào)用ClassLoader.getSystemClassLoader(),默認獲取的就是應(yīng)用程序類加載器;
  • Bootstrap 特殊點:它是 JVM 本地代碼實現(xiàn)的,在 Java 中獲取它會返回null。

2. 代碼驗證類加載器

通過簡單代碼查看不同類的加載器,直觀理解層級關(guān)系:

public class ClassLoaderDemo {
    public static void main(String[] args) {
        // 1. 核心類:由Bootstrap加載,輸出null
        ClassLoader bootstrapLoader = String.class.getClassLoader();
        System.out.println("String類加載器: " + bootstrapLoader);

        // 2. 系統(tǒng)類加載器(應(yīng)用類加載器)
        ClassLoader appLoader = ClassLoaderDemo.class.getClassLoader();
        System.out.println("自定義類加載器: " + appLoader);

        // 3. 應(yīng)用類加載器的父加載器:擴展類加載器
        ClassLoader extLoader = appLoader.getParent();
        System.out.println("應(yīng)用類加載器的父加載器: " + extLoader);

        // 4. 擴展類加載器的父加載器:Bootstrap,輸出null
        System.out.println("擴展類加載器的父加載器: " + extLoader.getParent());
    }
}

二、雙親委派機制

1. 定義

雙親委派機制是 JVM 類加載的默認規(guī)則:當一個類加載器收到類加載請求時,不會自己先嘗試加載,而是將請求向上委托給父加載器,遞歸向上直到頂層啟動類加載器;只有當父加載器無法加載該類時,子加載器才會嘗試自己加載。

2. 執(zhí)行流程(遞歸邏輯)

自定義類加載器收到加載請求,先檢查是否已加載該類,已加載則直接返回Class對象;

未加載則委托給父加載器(應(yīng)用類加載器)

應(yīng)用類加載器重復(fù)檢查邏輯,委托給擴展類加載器;

擴展類加載器重復(fù)檢查邏輯,委托給啟動類加載器;

啟動類加載器嘗試在自身加載路徑查找類:

  • 找到:直接加載并返回Class對象;
  • 未找到:向下回溯,讓子加載器嘗試加載;

若所有層級加載器都無法加載,拋出ClassNotFoundException。

3. 核心優(yōu)勢

避免類重復(fù)加載:保證同一個類在 JVM 中只被加載一次,生成唯一的Class對象;

保障 Java 核心 API 安全(沙箱安全):防止惡意代碼自定義核心類(如java.lang.String)替換 JDK 原生類,避免核心 API 被篡改;

層級管理類資源:規(guī)范不同范圍類的加載邊界,提升類加載的穩(wěn)定性。

4. 源碼層面解析

雙親委派的核心邏輯封裝在ClassLoaderloadClass(String name, boolean resolve)方法中(JDK8 源碼核心邏輯):

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 1. 檢查類是否已經(jīng)被當前加載器加載
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                // 2. 遞歸委托父加載器加載
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 3. 無父加載器,直接使用啟動類加載器
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 父加載器無法加載,捕獲異常
            }
            // 4. 父加載器加載失敗,當前加載器自行加載
            if (c == null) {
                c = findClass(name);
            }
        }
        // 解析類
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

關(guān)鍵方法:findClass()是子類的擴展點,默認拋出異常,自定義加載器需要重寫它。

三、打破雙親委派機制

1. 為什么要打破?

默認的雙親委派無法滿足所有場景,常見需求:

  • 加載自定義路徑 / 特殊來源的類(網(wǎng)絡(luò)字節(jié)碼、加密字節(jié)碼、模塊化 jar);
  • 實現(xiàn)類隔離(如 Web 容器 Tomcat、SPI 機制、熱部署);
  • 同一個應(yīng)用中需要加載同一個類的不同版本。

2. 打破的核心原理

雙親委派的邏輯寫在loadClass()方法中,重寫loadClass()方法,跳過向上委托的邏輯,就能直接打破該機制。

3. 標準實現(xiàn)方式:自定義類加載器

步驟

  • 繼承java.lang.ClassLoader抽象類;
  • 重寫loadClass()方法,破壞默認的雙親委派遞歸邏輯;
  • 重寫findClass()方法,實現(xiàn)自定義的類字節(jié)碼讀取邏輯。

完整示例代碼

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 打破雙親委派的自定義類加載器
 */
public class CustomClassLoader extends ClassLoader {
    // 自定義類加載路徑
    private final String classPath;

    public CustomClassLoader(String classPath) {
        // 關(guān)鍵:不指定父加載器(默認父加載器為系統(tǒng)類加載器,也可手動設(shè)置)
        this.classPath = classPath;
    }

    /**
     * 重寫loadClass,核心:跳過雙親委派的向上委托邏輯
     */
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 1. 檢查是否已加載
            Class<?> clazz = findLoadedClass(name);
            if (clazz != null) {
                return clazz;
            }

            // 2. 打破規(guī)則:核心類仍交給Bootstrap加載,避免JDK核心類加載異常
            if (name.startsWith("java.")) {
                return getSystemClassLoader().loadClass(name);
            }

            // 3. 不委托父加載器,直接自己加載
            try {
                clazz = findClass(name);
            } catch (Exception e) {
                // 加載失敗,降級為系統(tǒng)類加載器加載
                return super.loadClass(name, resolve);
            }

            if (resolve) {
                resolveClass(clazz);
            }
            return clazz;
        }
    }

    /**
     * 重寫findClass:從自定義路徑讀取字節(jié)碼
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassBytes(name);
        if (classData == null) {
            throw new ClassNotFoundException("無法找到類:" + name);
        }
        // 將字節(jié)碼轉(zhuǎn)換為Class對象
        return defineClass(name, classData, 0, classData.length);
    }

    /**
     * 從文件系統(tǒng)讀取.class字節(jié)碼
     */
    private byte[] getClassBytes(String className) {
        String path = classPath + "/" + className.replace(".", "/") + ".class";
        try (FileInputStream fis = new FileInputStream(path);
             ByteArrayOutputStream bos = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            return null;
        }
    }
}

測試代碼

public class TestCustomLoader {
    public static void main(String[] args) throws Exception {
        // 自定義加載路徑
        CustomClassLoader loader = new CustomClassLoader("D:/test/classes");
        // 加載自定義類
        Class<?> clazz = loader.loadClass("com.test.DemoClass");
        // 打印加載器,驗證為自定義加載器
        System.out.println("類加載器: " + clazz.getClassLoader());
    }
}

4. 行業(yè)中打破雙親委派的經(jīng)典場景

場景 1:Tomcat 等 Web 容器

  • 需求:一個 Web 服務(wù)器部署多個應(yīng)用,不同應(yīng)用可能依賴同一個類的不同版本,需要類隔離;
  • 實現(xiàn):每個 Web 應(yīng)用對應(yīng)一個獨立的WebAppClassLoader,優(yōu)先加載應(yīng)用自身的類,跳過雙親委派向上委托邏輯,避免類沖突。

場景 2:JDBC SPI 服務(wù)發(fā)現(xiàn)

  • JDK 的rt.jar中定義了java.sql.Driver接口(由 Bootstrap 加載),但具體驅(qū)動實現(xiàn)類(MySQL/PostgreSQL 驅(qū)動)在classpath下;
  • Bootstrap 加載器無法加載應(yīng)用層的驅(qū)動類,因此通過Thread.getContextClassLoader()(線程上下文類加載器)打破委派,讓啟動類加載器反向使用應(yīng)用類加載器加載實現(xiàn)類。

場景 3:熱部署 / 熱加載

  • 如 JRebel、Spring Boot DevTools,通過自定義類加載器重新加載修改后的類,繞過默認的緩存機制,實現(xiàn)代碼熱更新。

總結(jié)

核心知識點回顧:

  1. 四大類加載器:Bootstrap(頂層、C 實現(xiàn))→ Extension → Application → 自定義加載器,層級為組合關(guān)系而非繼承;
  2. 雙親委派機制:向上委托、向下加載,核心價值是防重復(fù)加載、保障核心 API 安全,邏輯在ClassLoader.loadClass()中;
  3. 打破方式重寫loadClass()方法跳過向上委托,配合自定義findClass()實現(xiàn)個性化加載;
  4. 工業(yè)級場景:Tomcat 類隔離、JDBC SPI、熱部署框架是打破雙親委派的典型應(yīng)用。

面試答題話術(shù)參考:

  • JVM 有四層類加載器,從頂層到底層分別是啟動類、擴展類、應(yīng)用類和自定義類加載器。
  • 雙親委派是類加載的默認規(guī)則,請求會遞歸向上委托給父加載器,父加載器無法加載時子加載器才會處理,主要作用是避免類重復(fù)和保證核心類安全。
  • 打破該機制的核心方法是自定義 ClassLoader 并重寫 loadClass 方法,跳過委托邏輯,像 Tomcat 的類隔離、JDBC 的 SPI 機制都是實際業(yè)務(wù)中打破該機制的經(jīng)典場景。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Boot maven框架搭建教程圖解

    Spring Boot maven框架搭建教程圖解

    這篇文章主要介紹了Spring Boot maven框架搭建教程圖解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • java實現(xiàn)html轉(zhuǎn)pdf方法步驟

    java實現(xiàn)html轉(zhuǎn)pdf方法步驟

    這篇文章主要給大家介紹了關(guān)于java實現(xiàn)html轉(zhuǎn)pdf方法的相關(guān)資料,要將HTML轉(zhuǎn)換成PDF,我們需要借助Java中的第三方庫,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • java線程中synchronized和Lock區(qū)別及介紹

    java線程中synchronized和Lock區(qū)別及介紹

    這篇文章主要為大家介紹了java線程中synchronized和Lock區(qū)別及介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • idea關(guān)閉vim編輯模式的操作

    idea關(guān)閉vim編輯模式的操作

    這篇文章主要介紹了idea關(guān)閉vim編輯模式的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • SpringBoot CountDownLatch多任務(wù)并行處理的實現(xiàn)方法

    SpringBoot CountDownLatch多任務(wù)并行處理的實現(xiàn)方法

    本篇文章主要介紹了SpringBoot CountDownLatch多任務(wù)并行處理的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • springboot2.0使用Hikari連接池的方法(替換druid)

    springboot2.0使用Hikari連接池的方法(替換druid)

    這篇文章主要介紹了springboot 2.0使用Hikari連接池的方法(替換druid),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Java使用Callable接口實現(xiàn)多線程的實例代碼

    Java使用Callable接口實現(xiàn)多線程的實例代碼

    這篇文章主要介紹了Java使用Callable接口實現(xiàn)多線程的實例代碼,實現(xiàn)Callable和實現(xiàn)Runnable類似,但是功能更強大,具體表現(xiàn)在可以在任務(wù)結(jié)束后提供一個返回值,Runnable不行,call方法可以拋出異,Runnable的run方法不行,需要的朋友可以參考下
    2023-08-08
  • idea導(dǎo)入springboot項目沒有maven的解決

    idea導(dǎo)入springboot項目沒有maven的解決

    這篇文章主要介紹了idea導(dǎo)入springboot項目沒有maven的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • springboot集成JWT之雙重token的實現(xiàn)

    springboot集成JWT之雙重token的實現(xiàn)

    本文主要介紹了springboot集成JWT之雙重token的實現(xiàn),前端使用accessToken進行登錄和驗證,后端使用refreshToken定期更新accessToken,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • type-aliases-package的用法解讀

    type-aliases-package的用法解讀

    在Mybatis的mapper.xml文件中使用type-aliases-package可以簡化resultType和parameterType的全類名指定,通過配置掃描指定包中的實體類,讓Mybatis自動識別這些實體
    2024-11-11

最新評論

深水埗区| 鄂伦春自治旗| 缙云县| 南康市| 叶城县| 河东区| 大足县| 南通市| 枣庄市| 南木林县| 安徽省| 阿勒泰市| 梅州市| 加查县| 安岳县| 阿克陶县| 土默特右旗| 香港 | 上林县| 六枝特区| 积石山| 富民县| 丘北县| 于都县| 惠来县| 天祝| 日照市| 翁源县| 北碚区| 新竹市| 疏附县| 巴林左旗| 崇州市| 曲松县| 大港区| 绥棱县| 威海市| 海城市| 陆河县| 潼南县| 五华县|