網站首頁 編程語言 正文
URLClassLoader詳解
ClassLoader翻譯過來就是類加載器,普通的java開發者其實用到的不多,但對于某些框架開發者來說卻非常常見。理解ClassLoader的加載機制,也有利于我們編寫出更高效的代碼。ClassLoader的具體作用就是將class文件加載到jvm虛擬機中去,程序就可以正確運行了。但是,jvm啟動的時候,并不會一次性加載所有的class文件,而是根據需要去動態加載。想想也是的,一次性加載那么多jar包那么多class,那內存不崩潰。本文的目的也是學習ClassLoader這種加載機制。。
- java類加載機制原理
- 雙親加載機制的優劣
java類加載機制
ClassLoader結構
java中內置了很多類加載器,本文只討論幾個核心類加載器:
ClassLoader:所有類加載器的基類,它是抽象的,定義了類加載最核心的操作。所有繼承與classloader的加載器,都會優先判斷是否被父類加載器加載過,防止多次加載,防止加載沖突。。。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//鎖,防止多次加載,所以jvm啟動巨慢
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
jdk 1.7為了提供并行加載class,提供ClassLoader.ParallelLoaders內部類,用來封裝一組并行能力的加載器類型。這個一般是用不到的,有興趣可以先看一下。但是需要知道ClassLoader是支持并行加載的。
private static class ParallelLoaders1
Bootstrap classLoader:位于java.lang.classload,所有的classload都要經過這個classload判斷是否已經被加載過,采用native code實現,是JVM的一部分,主要加載JVM自身工作需要的類,如java.lang._、java.uti._等; 這些類位于$JAVA_HOME/jre/lib/rt.jar。Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類,底層由C++編寫,已嵌入到了JVM內核當中,當JVM啟動后,Bootstrap ClassLoader也隨著啟動,負責加載完核心類庫后,并構造Extension ClassLoader和App ClassLoader類加載器。
/**
* Returns a class loaded by the bootstrap class loader;
* or return null if not found.
*/
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
// return null if not found
private native Class<?> findBootstrapClass(String name);
SecureClassLoader:繼承自ClassLoader,添加了關聯類源碼、關聯系統policy權限等支持。
public class SecureClassLoader extends ClassLoader1
URLClassLoader:繼承自SecureClassLoader,支持從jar文件和文件夾中獲取class,繼承于classload,加載時首先去classload里判斷是否由bootstrap classload加載過,1.7 新增實現closeable接口,實現在try 中自動釋放資源,但撲捉不了.close()異常
public class URLClassLoader extends SecureClassLoader implements Closeable1
ExtClassLoader:擴展類加載器,繼承自URLClassLoader繼承于urlclassload,擴展的class loader,加載位于$JAVA_HOME/jre/lib/ext目錄下的擴展jar。查看源碼可知其查找范圍為System.getProperty(“java.ext.dirs”)。
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
//System.getProperty("java.ext.dirs");
//在項目啟動時就加載所有的ext.dirs目錄下的文件,并將其初始化
final File[] var0 = getExtDirs();
try {
//AccessController.doPrivileged特權,讓程序突破當前域權限限制,臨時擴大訪問權限
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
MetaIndex.registerDirectory(var0[var2]);
}
return new Launcher.ExtClassLoader(var0);
}
});
} catch (PrivilegedActionException var2) {
throw (IOException)var2.getException();
}
}
ExtClassLoader 比較有意思的是,他使用的是頂級類(classloader)的loadclass方法,并沒有重寫,而且他的父親加載器是null。。 。
public ExtClassLoader(File[] var1) throws IOException {
//parents is null
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
AppClassLoader:應用類加載器,繼承自URLClassLoader,也叫系統類加載器(ClassLoader.getSystemClassLoader()可得到它),它負載加載應用的classpath下的類,查找范圍System.getProperty(“java.class.path”),通過-cp或-classpath指定的類都會被其加載,沒有完全遵循雙親委派模型的,它重的是loadClass方法
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
//ucp是SharedSecrets獲取的Java棧幀中存儲的類信息
if (this.ucp.knownToNotExist(var1)) {
//頂級類classloader加載的信息
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
//link過程,Class載入必須link,link指的是把單一的Class加入到有繼承關系的類樹中,不link一切都無從談起了
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
Launcher:java程序入口,負責實例化相關class,ExtClassLoader和AppClassLoader都是其內部實現類。。。
首先獲取bootClassPath 的位置信息,交付于父類Classloader去加載位于$JAVA_HOME/jre/lib/rt.jar 的類
private static String bootClassPath = System.getProperty("sun.boot.class.path");
public static URLClassPath getBootstrapClassPath() {
return Launcher.BootClassPathHolder.bcp;
}
實例化相關ExtClassLoader和AppClassLoader,單線程運行,內部加載會對class文件加鎖(所以啟動慢的原因??)
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//實例化ExtClassLoader,
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//實例化AppClassLoader,并把ExtClassLoader置為父類
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
//為線程設置上下文加載器,防止線程多次加載類
Thread.currentThread().setContextClassLoader(this.loader);
//加載安全管理
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
;
} catch (InstantiationException var6) {
;
} catch (ClassNotFoundException var7) {
;
} catch (ClassCastException var8) {
;
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
ClassLoader運行結構
主要基于 雙親加載機制
雙親加載機制:主要體現在ClassLoader的loadClass()方法中,思路很簡單:先檢查是否已經被加載過,若沒有加載則調用父類加載器的loadClass()方法,若父類加載器為空則默認使用啟動類加載器作為父類加載器。如果父類加載器加載失敗,拋出ClassNotFoundException異常后,調用自己的findClass()方法進行加載。
雙親與判斷等于:一般自定義的Class Loader可以從java.lang.ClassLoader繼承,不同classloader加載相同的類,他們在內存也不是相等的,即它們不能互相轉換,會直接拋異常。java.lang.ClassLoader的核心加載方法是loadClass方法
Class clazz = null;
ClassLoader classLoader;
try {
classLoader = new SpecialClassLoader ();
clazz = classLoader.loadClass("Hello");
System.out.println(clazz);
System.out.println(clazz.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class helloClazz;
helloClazz = Hello.class;
System.out.println("helloClazz:" + helloClazz);
System.out.println(helloClazz.getClassLoader());
System.out.println(helloClazz == clazz);
System.out.println(helloClazz.equals(clazz));
運行結果:
loadClass:Hello
specialLoadClass:test/Hello.class
loadClass:java.lang.Object
—resolveClass–
class com.test.javatechnology.classloader.test.Hello
com.test.javatechnology.classloader.SpecialClassLoader@106d69c
helloClazz:class com.test.javatechnology.classloader.test.Hello
sun.misc.Launcher$AppClassLoader@e2f2a
false
false
原文鏈接:https://blog.csdn.net/qq_43985303/article/details/127704907
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-03-17 .NET?6開發TodoList應用實現系列背景_實用技巧
- 2023-07-16 oracle 創建定時任務
- 2022-09-04 使用Django+Pytest搭建在線自動化測試平臺_python
- 2022-05-14 面試分析分布式架構Redis熱點key大Value解決方案_Redis
- 2022-09-15 Android?Jetpack庫剖析之ViewModel組件篇_Android
- 2022-09-09 通過jmeter壓測surging的方法_實用技巧
- 2022-07-28 Redis特殊數據類型HyperLogLog基數統計算法講解_Redis
- 2022-04-08 iOS實現計算器小功能_IOS
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支