摘要:加載器種類啟動類加載器在中用來加載自身需要的類,實現(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 中的有些許不同。
ClassLoaderClassLoader 是類加載器的頂級父類,其核心的方法主要是 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
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區(qū)分點在于工作方向的側(cè)重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內(nèi)容分為三個模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計算機基礎(chǔ) JVM原理 多線程 設(shè)計模式 數(shù)據(jù)結(jié)構(gòu)與算法 應用模塊: 常用工具集 ...
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領(lǐng)導們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:請注意,我們在聊聊單元測試遇到問題多思考多查閱多驗證,方能有所得,再勤快點樂于分享,才能寫出好文章。單元測試是指對軟件中的最小可測試單元進行檢查和驗證。 JAVA容器-自問自答學HashMap 這次我和大家一起學習HashMap,HashMap我們在工作中經(jīng)常會使用,而且面試中也很頻繁會問到,因為它里面蘊含著很多知識點,可以很好的考察個人基礎(chǔ)。但一個這么重要的東西,我為什么沒有在一開始...
摘要:再附一部分架構(gòu)面試視頻講解本文已被開源項目學習筆記總結(jié)移動架構(gòu)視頻大廠面試真題項目實戰(zhàn)源碼收錄 Java反射(一)Java反射(二)Java反射(三)Java注解Java IO(一)Java IO(二)RandomAccessFileJava NIOJava異常詳解Java抽象類和接口的區(qū)別Java深拷貝和淺拷...
摘要:當一個文件是通過網(wǎng)絡(luò)傳輸并且可能會進行相應的加密操作時,需要先對文件進行相應的解密后再加載到內(nèi)存中,這種情況下也需要編寫自定義的并實現(xiàn)相應的邏輯 Java虛擬機中的類加載有三大步驟:,鏈接,初始化.其中加載是指查找字節(jié)流(也就是由Java編譯器生成的class文件)并據(jù)此創(chuàng)建類的過程,這中間我們需要借助類加載器來查找字節(jié)流. Java虛擬機默認類加載器 Java虛擬機提供了3種類加載器...
閱讀 2050·2021-11-08 13:22
閱讀 2510·2021-09-04 16:40
閱讀 1155·2021-09-03 10:29
閱讀 1723·2019-08-30 15:44
閱讀 2127·2019-08-30 11:13
閱讀 2795·2019-08-29 17:07
閱讀 1972·2019-08-29 14:22
閱讀 1252·2019-08-26 14:00