摘要:加載器種類啟動類加載器在中用來加載自身需要的類,實現,用來加載。那么就能保證的類會被優先加載,限制了使用者對系統的影響。這種方式下就完成類加載器的雙親委派機制此處會將作為參數傳入進去實際上是調用了方法
Class 文件的裝載流程 (類加載過程)
加載 -> 連接 (驗證 -> 準備 -> 解析) -> 初始化 -> 使用 -> 卸載加載
加載階段,jvm 會通過類名獲取到此類的字節碼文件(.class 文件), 然后將該文件中的數據結構轉存到內存里(轉化為運行時方法區內的數據結構), 最后在堆中生成一個代表該類的 Class 對象,用于后期使用者創建對象或者調用相關方法。驗證
驗證階段用于保證 Class 文件符合 jvm 規范,如果驗證失敗會拋出 error。準備
在該階段虛擬機會給類對象的靜態成員變量配置內存空間,并賦初始值。解析
將類/接口/字段/方法中的號引用替換為直接引用。初始化
虛擬機會調用類對象的初始化方法來進行類變量的賦值。Class 文件被裝載的條件
必須要有類去主動使用該 Class。 方式有: 使用 new 關鍵字、反射、克隆、反序列化; 調用類的靜態方法; 調用一個類的子類的時候會初始化其父類; 包含 main() 方法的類。 被動使用則不會去裝載 Class。 方式有: 調用了其父類的靜態方法。
總結:
jvm 秉持了實用主義理念,對于沒有用到的 Class 不會進行裝載。 但是在 java 代碼的啟動環節會加載一些使用到的類。加載器種類 啟動類加載器(Bootstrap ClassLoader):
在 jdk8 中用來加載 jvm 自身需要的類,c++ 實現,用來加載 rt.jar。 在 jdk9 之后的 jdk 中,Bootstrap ClassLoader 主要用來加載 java.base 中的核心系統類。擴展類加載器(ExtClassLoader):
jdk8 中用來加載 ${JAVA_HOME}/lib/ext 目錄下的類。 在 jdk9 中已經被移除。模塊加載器(PlatformClassLoader):
jdk9 之后用來代替 ExtClassLoader 的加載器,用來加載 jdk 中的非核心模塊類。應用程序類加載器(AppClassLoader):
用來加載一般的應用類。自定義加載器:
使用者自己定義的,一般繼承 java.lang.ClassLoader 的類。雙親委派機制
任意一個 ClassLoader 在嘗試加載一個類的時候,都會先嘗試調用其父類的相關方法去加載類,如果其父類不能加載該類,則交由子類去完成。 這樣的好處:對于任意使用者自定義的 ClassLoader,都會先去嘗試讓 jvm 的 Bootstrap ClassLoader 去嘗試加載(自定義的 ClassLoader 都繼承了它們)。那么就能保證 jvm 的類會被優先加載,限制了使用者對 jvm 系統的影響。源碼
源碼探究使用 jdk11,與 jdk8 中的有些許不同。
ClassLoaderClassLoader 是類加載器的頂級父類,其核心的方法主要是 loadClass(...) 方法:
// ClassLoader.class protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException{ // 加鎖,保證線程安全 synchronized (getClassLoadingLock(name)) { // 先去找一次 class 是否已經被加載了,如果已經被加載了就不用重復加載了 // 此方法的核心邏輯由 c++ 實現 Class> c = findLoadedClass(name); // 沒有被加載的情況 if (c == null) { long t0 = System.nanoTime(); // 記錄時間 try { // 此處體現雙親委派機制 // 如果該加載器存在父加載器,就會先去調用父加載器的相關方法 // 如果沒有父加載器,就去調用 Bootstrap 加載器 if (parent != null) { c = parent.loadClass(name, false); } else { // 調用 BootstrapClassLoader,此方法的核心邏輯是 c++ 實現的 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { } // 如果依舊加載不到,那么就說明父加載器仍然加載不到信息 // 那么就需要指定的加載器自己去加載了 if (c == null) { long t1 = System.nanoTime(); // 該加載器加載類文件的核心邏輯 // 該方法在 ClassLoader 中是留空的,需要子類按照自身的邏輯去實現 c = findClass(name); // 此處做一些信息記錄,和主邏輯無關 PerfCounter.getParentDelegationTime().addTime(t1 - t0); PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); PerfCounter.getFindClasses().increment(); } } if (resolve) { // 解析 class,也是留空的,需要子類去實現 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(...) 方法,但是核心是調用 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 是否已經被加載了,此方法是 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 { // 先調用父加載器的相關方法去加載一次 if (parent != null) { c = parent.loadClassOrNull(cn); } // 如果沒加載到,則用當前加載器去加載 if (c == null && hasClassPath() && VM.isModuleSystemInited(){ // 此方法內會調用到 defineClass(...) 方法去加載類文件 c = findClassOnClassPathOrNull(cn); } } } // 解析 class if (resolve && c != null) resolveClass(c); return c; } }
該加載器中還有一個加載 class 字節碼的方法:
// 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 的相關方法去加載 ByteBuffer bb = res.getByteBuffer(); if (bb != null) { CodeSigner[] signers = res.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); // 該方法最后會調用 ClassLoader 內的 native 方法 return defineClass(cn, bb, cs); } else { byte[] b = res.getBytes(); CodeSigner[] signers = res.getCodeSigners(); CodeSource cs = new CodeSource(url, signers); // 該方法最后會調用 ClassLoader 內的 native 方法 return defineClass(cn, b, 0, b.length, cs); } }BootClassLoader
BootClassLoader 是 ClassLoaders 的一個靜態內部類,雖然它從代碼實現上是 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 的一個靜態內部類,從功能上說它是 BootClassLoader 的子類,同時也是 AppClassLoader 的 parent 類。PlatformClassLoader 主要用來加載一些 module:
// ClassLoader.class private static class PlatformClassLoader extends BuiltinClassLoader { static { if (!ClassLoader.registerAsParallelCapable()) throw new InternalError(); } // 此處會將 BootClassLoader 作為 parent 參數傳入進去 PlatformClassLoader(BootClassLoader parent) { super("platform", parent, null); } // 加載 module private Package definePackage(String pn, Module module) { return JLA.definePackage(this, pn, module); } }AppClassLoader
AppClassLoader 的核心方法是 loadClass(...),最終會調用到 BuiltinClassLoader.loadClassOrNull(...) 方法,而此方法內部又會調用到 PlatformClassLoader.loadClass(...) 方法;然后實際上 PlatformClassLoader 內部又會去調用 BootClassLoader 的 loadClassOrNull(...) 方法。這種方式下就完成類加載器的雙親委派機制:
// ClassLoader.class private static class AppClassLoader extends BuiltinClassLoader { static { if (!ClassLoader.registerAsParallelCapable()) throw new InternalError(); } final URLClassPath ucp; // 此處會將 PlatformClassLoader 作為 parent 參數傳入進去 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)); } } // 實際上是調用了 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); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76281.html
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
摘要:再附一部分架構面試視頻講解本文已被開源項目學習筆記總結移動架構視頻大廠面試真題項目實戰源碼收錄 Java反射(一)Java反射(二)Java反射(三)Java注解Java IO(一)Java IO(二)RandomAccessFileJava NIOJava異常詳解Java抽象類和接口的區別Java深拷貝和淺拷...
摘要:當一個文件是通過網絡傳輸并且可能會進行相應的加密操作時,需要先對文件進行相應的解密后再加載到內存中,這種情況下也需要編寫自定義的并實現相應的邏輯 Java虛擬機中的類加載有三大步驟:,鏈接,初始化.其中加載是指查找字節流(也就是由Java編譯器生成的class文件)并據此創建類的過程,這中間我們需要借助類加載器來查找字節流. Java虛擬機默認類加載器 Java虛擬機提供了3種類加載器...
閱讀 2036·2021-11-08 13:22
閱讀 2500·2021-09-04 16:40
閱讀 1144·2021-09-03 10:29
閱讀 1709·2019-08-30 15:44
閱讀 2120·2019-08-30 11:13
閱讀 2785·2019-08-29 17:07
閱讀 1963·2019-08-29 14:22
閱讀 1244·2019-08-26 14:00