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

JVM類(lèi)加載機(jī)制與雙親委派使用及說(shuō)明

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

一、JVM 四大類(lèi)加載器

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

Java 默認(rèn)提供了四層層級(jí)結(jié)構(gòu)的類(lèi)加載器,從上到下依次為:?jiǎn)?dòng)類(lèi)加載器、擴(kuò)展類(lèi)加載器、應(yīng)用程序類(lèi)加載器、自定義類(lèi)加載器。

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

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

擴(kuò)展類(lèi)加載器

(jdk9+后改名為平臺(tái)類(lèi)加載器)

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

補(bǔ)充關(guān)鍵說(shuō)明

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

2. 代碼驗(yàn)證類(lèi)加載器

通過(guò)簡(jiǎn)單代碼查看不同類(lèi)的加載器,直觀理解層級(jí)關(guān)系:

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

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

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

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

二、雙親委派機(jī)制

1. 定義

雙親委派機(jī)制是 JVM 類(lèi)加載的默認(rèn)規(guī)則:當(dāng)一個(gè)類(lèi)加載器收到類(lèi)加載請(qǐng)求時(shí),不會(huì)自己先嘗試加載,而是將請(qǐng)求向上委托給父加載器,遞歸向上直到頂層啟動(dòng)類(lèi)加載器;只有當(dāng)父加載器無(wú)法加載該類(lèi)時(shí),子加載器才會(huì)嘗試自己加載。

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

自定義類(lèi)加載器收到加載請(qǐng)求,先檢查是否已加載該類(lèi),已加載則直接返回Class對(duì)象;

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

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

擴(kuò)展類(lèi)加載器重復(fù)檢查邏輯,委托給啟動(dòng)類(lèi)加載器;

啟動(dòng)類(lèi)加載器嘗試在自身加載路徑查找類(lèi):

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

若所有層級(jí)加載器都無(wú)法加載,拋出ClassNotFoundException。

3. 核心優(yōu)勢(shì)

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

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

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

4. 源碼層面解析

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

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

關(guān)鍵方法:findClass()是子類(lèi)的擴(kuò)展點(diǎn),默認(rèn)拋出異常,自定義加載器需要重寫(xiě)它。

三、打破雙親委派機(jī)制

1. 為什么要打破?

默認(rèn)的雙親委派無(wú)法滿(mǎn)足所有場(chǎng)景,常見(jiàn)需求:

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

2. 打破的核心原理

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

3. 標(biāo)準(zhǔn)實(shí)現(xiàn)方式:自定義類(lèi)加載器

步驟

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

完整示例代碼

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

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

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

    /**
     * 重寫(xiě)loadClass,核心:跳過(guò)雙親委派的向上委托邏輯
     */
    @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ī)則:核心類(lèi)仍交給Bootstrap加載,避免JDK核心類(lèi)加載異常
            if (name.startsWith("java.")) {
                return getSystemClassLoader().loadClass(name);
            }

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

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

    /**
     * 重寫(xiě)findClass:從自定義路徑讀取字節(jié)碼
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassBytes(name);
        if (classData == null) {
            throw new ClassNotFoundException("無(wú)法找到類(lèi):" + name);
        }
        // 將字節(jié)碼轉(zhuǎn)換為Class對(duì)象
        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;
        }
    }
}

測(cè)試代碼

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

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

場(chǎng)景 1:Tomcat 等 Web 容器

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

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

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

場(chǎng)景 3:熱部署 / 熱加載

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

總結(jié)

核心知識(shí)點(diǎn)回顧:

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

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

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

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

相關(guān)文章

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

    Spring Boot maven框架搭建教程圖解

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

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

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

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

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

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

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

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

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

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

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

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

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

    idea導(dǎo)入springboot項(xiàng)目沒(méi)有maven的解決

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

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

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

    type-aliases-package的用法解讀

    在Mybatis的mapper.xml文件中使用type-aliases-package可以簡(jiǎn)化resultType和parameterType的全類(lèi)名指定,通過(guò)配置掃描指定包中的實(shí)體類(lèi),讓Mybatis自動(dòng)識(shí)別這些實(shí)體
    2024-11-11

最新評(píng)論

阳东县| 苏尼特右旗| 南昌市| 砚山县| 特克斯县| 伊春市| 嵩明县| 衡东县| 浠水县| 盐边县| 周至县| 鄢陵县| 盱眙县| 肃宁县| 那曲县| 贵港市| 平舆县| 砀山县| 新巴尔虎右旗| 双江| 册亨县| 大余县| 原平市| 会东县| 镇赉县| 英德市| 广河县| 大丰市| 于都县| 英吉沙县| 利川市| 屏东市| 连城县| 麟游县| 晋中市| 新干县| 罗田县| 雅江县| 彭州市| 石景山区| 修水县|