成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JVM_類加載機制詳解

MasonEast / 3607人閱讀

摘要:加載器種類啟動類加載器在中用來加載自身需要的類,實現(xiàn),用來加載。那么就能保證的類會被優(yōu)先加載,限制了使用者對系統(tǒng)的影響。這種方式下就完成類加載器的雙親委派機制此處會將作為參數(shù)傳入進去實際上是調(diào)用了方法

Class 文件的裝載流程 (類加載過程)
加載 -> 連接 (驗證 -> 準備 -> 解析) -> 初始化 -> 使用 -> 卸載
加載
加載階段,jvm 會通過類名獲取到此類的字節(jié)碼文件(.class 文件),
然后將該文件中的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)存到內(nèi)存里(轉(zhuǎn)化為運行時方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)),
最后在堆中生成一個代表該類的 Class 對象,用于后期使用者創(chuàng)建對象或者調(diào)用相關(guān)方法。
驗證
驗證階段用于保證 Class 文件符合 jvm 規(guī)范,如果驗證失敗會拋出 error。
準備
在該階段虛擬機會給類對象的靜態(tài)成員變量配置內(nèi)存空間,并賦初始值。
解析
將類/接口/字段/方法中的號引用替換為直接引用。
初始化
虛擬機會調(diào)用類對象的初始化方法來進行類變量的賦值。
Class 文件被裝載的條件
必須要有類去主動使用該 Class。
方式有:
使用 new 關(guān)鍵字、反射、克隆、反序列化;
調(diào)用類的靜態(tài)方法;
調(diào)用一個類的子類的時候會初始化其父類;
包含 main() 方法的類。

被動使用則不會去裝載 Class。
方式有:
調(diào)用了其父類的靜態(tài)方法。

總結(jié):

jvm 秉持了實用主義理念,對于沒有用到的 Class 不會進行裝載。
但是在 java 代碼的啟動環(huán)節(jié)會加載一些使用到的類。
加載器種類 啟動類加載器(Bootstrap ClassLoader):
在 jdk8 中用來加載 jvm 自身需要的類,c++ 實現(xiàn),用來加載 rt.jar。
在 jdk9 之后的 jdk 中,Bootstrap ClassLoader 主要用來加載 java.base 中的核心系統(tǒng)類。
擴展類加載器(ExtClassLoader):
jdk8 中用來加載 ${JAVA_HOME}/lib/ext 目錄下的類。
在 jdk9 中已經(jīng)被移除。
模塊加載器(PlatformClassLoader):
jdk9 之后用來代替 ExtClassLoader 的加載器,用來加載 jdk 中的非核心模塊類。
應用程序類加載器(AppClassLoader):
用來加載一般的應用類。
自定義加載器:
使用者自己定義的,一般繼承 java.lang.ClassLoader 的類。
雙親委派機制
任意一個 ClassLoader 在嘗試加載一個類的時候,都會先嘗試調(diào)用其父類的相關(guān)方法去加載類,如果其父類不能加載該類,則交由子類去完成。

這樣的好處:對于任意使用者自定義的 ClassLoader,都會先去嘗試讓 jvm 的 Bootstrap ClassLoader 去嘗試加載(自定義的 ClassLoader 都繼承了它們)。那么就能保證 jvm 的類會被優(yōu)先加載,限制了使用者對 jvm 系統(tǒng)的影響。
源碼

源碼探究使用 jdk11,與 jdk8 中的有些許不同。

ClassLoader

ClassLoader 是類加載器的頂級父類,其核心的方法主要是 loadClass(...) 方法:

// ClassLoader.class
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException{
    // 加鎖,保證線程安全
    synchronized (getClassLoadingLock(name)) {
        // 先去找一次 class 是否已經(jīng)被加載了,如果已經(jīng)被加載了就不用重復加載了
        // 此方法的核心邏輯由 c++ 實現(xiàn)
        Class c = findLoadedClass(name);
        // 沒有被加載的情況
        if (c == null) {
            long t0 = System.nanoTime(); // 記錄時間
            try {
                // 此處體現(xiàn)雙親委派機制
                // 如果該加載器存在父加載器,就會先去調(diào)用父加載器的相關(guān)方法
                // 如果沒有父加載器,就去調(diào)用 Bootstrap 加載器
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 調(diào)用 BootstrapClassLoader,此方法的核心邏輯是 c++ 實現(xiàn)的
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {

            }

            // 如果依舊加載不到,那么就說明父加載器仍然加載不到信息
            // 那么就需要指定的加載器自己去加載了
            if (c == null) {

                long t1 = System.nanoTime();
                
                // 該加載器加載類文件的核心邏輯
                // 該方法在 ClassLoader 中是留空的,需要子類按照自身的邏輯去實現(xiàn)
                c = findClass(name);

                // 此處做一些信息記錄,和主邏輯無關(guān)
                PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                PerfCounter.getFindClasses().increment();
            }
        }
        
        if (resolve) {
            // 解析 class,也是留空的,需要子類去實現(xiàn)
            resolveClass(c);
        }
        return c;
    }
}
BuiltinClassLoader

BuiltinClassLoader 是 jdk9 中代替 URLClassLoader 的加載器,是 PlatformClassLoader 與 AppClassLoader 的父類。其繼承了 SecureClassLoader,其核心的方法主要是 loadClassOrNull(...) 方法:

// BuiltinClassLoader.class

// step 1
@Override
protected Class loadClass(String cn, boolean resolve) throws ClassNotFoundException{
    // 復寫了 loadClass(...) 方法,但是核心是調(diào)用 loadClassOrNull(...)
    Class c = loadClassOrNull(cn, resolve);
    if (c == null)
        throw new ClassNotFoundException(cn);
    return c;
}

// step 2
protected Class loadClassOrNull(String cn, boolean resolve) {
    // 加鎖,保證線程安全
    synchronized (getClassLoadingLock(cn)) {
        // 先去找一次 class 是否已經(jīng)被加載了,此方法是 ClassLoader 中的
        Class c = findLoadedClass(cn);

        if (c == null) {

            // 這里會需要去先加載模塊信息
            LoadedModule loadedModule = findLoadedModule(cn);
            if (loadedModule != null) {
                BuiltinClassLoader loader = loadedModule.loader();
                if (loader == this) {
                    if (VM.isModuleSystemInited()) {
                        c = findClassInModuleOrNull(loadedModule, cn);
                    }
                } else {
                    c = loader.loadClassOrNull(cn);
                }
            } else {

                // 先調(diào)用父加載器的相關(guān)方法去加載一次
                if (parent != null) {
                    c = parent.loadClassOrNull(cn);
                }

                // 如果沒加載到,則用當前加載器去加載
                if (c == null && hasClassPath() && VM.isModuleSystemInited(){
                    // 此方法內(nèi)會調(diào)用到 defineClass(...) 方法去加載類文件
                    c = findClassOnClassPathOrNull(cn);
                }
            }

        }

        // 解析 class
        if (resolve && c != null)
            resolveClass(c);

        return c;
    }
}

該加載器中還有一個加載 class 字節(jié)碼的方法:

// BuiltinClassLoader.class
private Class defineClass(String cn, Resource res) throws IOException{
    
    URL url = res.getCodeSourceURL();

    // 先解析這個 class 的路徑
    int pos = cn.lastIndexOf(".");
    if (pos != -1) {
        String pn = cn.substring(0, pos);
        Manifest man = res.getManifest();
        defineOrCheckPackage(pn, man, url);
    }

    // 這里會將 class 讀取出來成一個 byte[] 字符串,并通過 jvm 的相關(guān)方法去加載
    ByteBuffer bb = res.getByteBuffer();
    if (bb != null) {
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        // 該方法最后會調(diào)用 ClassLoader 內(nèi)的 native 方法
        return defineClass(cn, bb, cs);
    } else {
        byte[] b = res.getBytes();
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        // 該方法最后會調(diào)用 ClassLoader 內(nèi)的 native 方法
        return defineClass(cn, b, 0, b.length, cs);
    }
}
BootClassLoader

BootClassLoader 是 ClassLoaders 的一個靜態(tài)內(nèi)部類,雖然它從代碼實現(xiàn)上是 BuiltinClassLoader 的子類,但是從功能上說它是 PlatformClassLoader 的 parent 類:

// ClassLoader.class
private static class BootClassLoader extends BuiltinClassLoader {
    BootClassLoader(URLClassPath bcp) {
        super(null, null, bcp);
    }

    // 復寫了 BuiltinClassLoader 中的 loadClassOrNull(...) 方法
    @Override
    protected Class loadClassOrNull(String cn) {
        return JLA.findBootstrapClassOrNull(this, cn);
    }
};
PlatformClassLoader

PlatformClassLoader 也是 ClassLoaders 的一個靜態(tài)內(nèi)部類,從功能上說它是 BootClassLoader 的子類,同時也是 AppClassLoader 的 parent 類。PlatformClassLoader 主要用來加載一些 module:

// ClassLoader.class
private static class PlatformClassLoader extends BuiltinClassLoader {
    static {
        if (!ClassLoader.registerAsParallelCapable())
            throw new InternalError();
    }

    // 此處會將 BootClassLoader 作為 parent 參數(shù)傳入進去
    PlatformClassLoader(BootClassLoader parent) {
        super("platform", parent, null);
    }

    // 加載 module
    private Package definePackage(String pn, Module module) {
        return JLA.definePackage(this, pn, module);
    }
}
AppClassLoader

AppClassLoader 的核心方法是 loadClass(...),最終會調(diào)用到 BuiltinClassLoader.loadClassOrNull(...) 方法,而此方法內(nèi)部又會調(diào)用到 PlatformClassLoader.loadClass(...) 方法;然后實際上 PlatformClassLoader 內(nèi)部又會去調(diào)用 BootClassLoader 的 loadClassOrNull(...) 方法。這種方式下就完成類加載器的雙親委派機制:

// ClassLoader.class
private static class AppClassLoader extends BuiltinClassLoader {
    static {
        if (!ClassLoader.registerAsParallelCapable())
            throw new InternalError();
    }

    final URLClassPath ucp;

    // 此處會將 PlatformClassLoader 作為 parent 參數(shù)傳入進去
    AppClassLoader(PlatformClassLoader parent, URLClassPath ucp) {
        super("app", parent, ucp);
        this.ucp = ucp;
    }

    @Override
    protected Class loadClass(String cn, boolean resolve) throws ClassNotFoundException{
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = cn.lastIndexOf(".");
            if (i != -1) {
                sm.checkPackageAccess(cn.substring(0, i));
            }
        }
        // 實際上是調(diào)用了 BuiltinClassLoader.loadClassOrNull(...) 方法
        return super.loadClass(cn, resolve);
    }

    @Override
    protected PermissionCollection getPermissions(CodeSource cs) {
        PermissionCollection perms = super.getPermissions(cs);
        perms.add(new RuntimePermission("exitVM"));
        return perms;
    }


    void appendToClassPathForInstrumentation(String path) {
        ucp.addFile(path);
    }


    private Package definePackage(String pn, Module module) {
        return JLA.definePackage(this, pn, module);
    }


    protected Package defineOrCheckPackage(String pn, Manifest man, URL url) {
        return super.defineOrCheckPackage(pn, man, url);
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76281.html

相關(guān)文章

  • Java面試 32個核心必考點完全解析

    摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區(qū)分點在于工作方向的側(cè)重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內(nèi)容分為三個模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計算機基礎(chǔ) JVM原理 多線程 設(shè)計模式 數(shù)據(jù)結(jié)構(gòu)與算法 應用模塊: 常用工具集 ...

    JiaXinYi 評論0 收藏0
  • Java學習路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強)

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領(lǐng)導們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0
  • Java經(jīng)典

    摘要:請注意,我們在聊聊單元測試遇到問題多思考多查閱多驗證,方能有所得,再勤快點樂于分享,才能寫出好文章。單元測試是指對軟件中的最小可測試單元進行檢查和驗證。 JAVA容器-自問自答學HashMap 這次我和大家一起學習HashMap,HashMap我們在工作中經(jīng)常會使用,而且面試中也很頻繁會問到,因為它里面蘊含著很多知識點,可以很好的考察個人基礎(chǔ)。但一個這么重要的東西,我為什么沒有在一開始...

    xcold 評論0 收藏0
  • 四年來Android面試大綱,作為一個Android程序員

    摘要:再附一部分架構(gòu)面試視頻講解本文已被開源項目學習筆記總結(jié)移動架構(gòu)視頻大廠面試真題項目實戰(zhàn)源碼收錄 Java反射(一)Java反射(二)Java反射(三)Java注解Java IO(一)Java IO(二)RandomAccessFileJava NIOJava異常詳解Java抽象類和接口的區(qū)別Java深拷貝和淺拷...

    不知名網(wǎng)友 評論0 收藏0
  • Java加載詳解

    摘要:當一個文件是通過網(wǎng)絡(luò)傳輸并且可能會進行相應的加密操作時,需要先對文件進行相應的解密后再加載到內(nèi)存中,這種情況下也需要編寫自定義的并實現(xiàn)相應的邏輯 Java虛擬機中的類加載有三大步驟:,鏈接,初始化.其中加載是指查找字節(jié)流(也就是由Java編譯器生成的class文件)并據(jù)此創(chuàng)建類的過程,這中間我們需要借助類加載器來查找字節(jié)流. Java虛擬機默認類加載器 Java虛擬機提供了3種類加載器...

    Baaaan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<