JVM的類加載器和雙親委派模式你了解嗎
類加載器
Java虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)有意把類加載階段中的“通過(guò)一個(gè)類的全限定名來(lái)獲取描述該類的二進(jìn)制字節(jié)流”這個(gè)動(dòng)作放到Java虛擬機(jī)外部去實(shí)現(xiàn),以便讓應(yīng)用程序自己決定如何去獲取所需的類。實(shí)現(xiàn)這個(gè)動(dòng)作的代碼被稱為“類加載器”(ClassLoader)。
對(duì)于任意一個(gè)類,都必須由加載它的類加載器和這個(gè)類本身一起共同確立其在Java虛擬機(jī)中的唯一性,每一個(gè)類加載器,都擁有一個(gè)獨(dú)立的類名稱空間。這句話可以表達(dá)得更通俗一些:比較兩個(gè)類是否“相等”,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義,否則,即使這兩個(gè)類來(lái)源于同一個(gè)Class文件,被同一個(gè)Java虛擬機(jī)加載,只要加載它們的類加載器不同,那這兩個(gè)類就必定不相等。
| 名稱 | 加載的類 | 說(shuō)明 |
|---|---|---|
| Bootstrap ClassLoader(啟動(dòng)類加載器) | JAVA_HOME/jre/lib | 無(wú)法直接訪問(wèn) |
| Extension ClassLoader(拓展類加載器) | JAVA_HOME/jre/lib/ext | 上級(jí)為Bootstrap,顯示為null |
| Application ClassLoader(應(yīng)用程序類加載器) | classpath | 上級(jí)為Extension |
| 自定義類加載器 | 自定義 | 上級(jí)為Application |
1、啟動(dòng)類加載器
可通過(guò)在控制臺(tái)輸入指令,使得類被啟動(dòng)類加器加載,它是用C++寫的,看不到源碼;其他類加載器是用Java寫的,說(shuō)白了就是一些Java類,比如擴(kuò)展類加載器、應(yīng)用類加載器。
//查詢所有被啟動(dòng)類加載器加載的類
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL url : urls) {
System.out.println(url);
}
//查詢到的結(jié)果 file:/C:/Program%20Files/Java/jre1.8.0_131/lib/resources.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/rt.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/sunrsasign.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/jsse.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/jce.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/charsets.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/jfr.jar file:/C:/Program%20Files/Java/jre1.8.0_131/classes
由上可以看出啟動(dòng)類加載的都是jre和jre/lib目錄下的核心庫(kù),具體路徑要看你的jre安裝在哪里。
2、拓展類加載器
如果classpath和JAVA_HOME/jre/lib/ext 下有同名類,加載時(shí)會(huì)使用拓展類加載器加載。當(dāng)應(yīng)用程序類加載器發(fā)現(xiàn)拓展類加載器已將該同名類加載過(guò)了,則不會(huì)再次加載
URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader().getParent()).getURLs();
for (URL url : urls) {
System.out.println(url);
}
file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/access-bridge-64.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/cldrdata.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/dnsns.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/dns_sd.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/jaccess.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/jfxrt.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/localedata.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/nashorn.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunec.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunjce_provider.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunmscapi.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunpkcs11.jar file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/zipfs.jar
這些類庫(kù)具體是什么不重要,只需要知道不同的類庫(kù)可能是被不同的類加載器加載的。
3、應(yīng)用程序類加載器
URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
for (URL url : urls) {
System.out.println(url);
}
file:/{項(xiàng)目工程目錄}/bin/
這是當(dāng)前java工程的bin目錄,也就是我們自己的Java代碼編譯成的class文件所在。
4、雙親委派模式
雙親委派模式,調(diào)用類加載器ClassLoader 的 loadClass 方法時(shí),查找類的規(guī)則。
loadClass源碼
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先查找該類是否已經(jīng)被該類加載器加載過(guò)了
Class<?> c = findLoadedClass(name);
//如果沒(méi)有被加載過(guò)
if (c == null) {
long t0 = System.nanoTime();
try {
//看是否被它的上級(jí)加載器加載過(guò)了 Extension的上級(jí)是Bootstarp,但它顯示為null
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//看是否被啟動(dòng)類加載器加載過(guò)
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
//捕獲異常,但不做任何處理
}
if (c == null) {
//如果還是沒(méi)有找到,先讓拓展類加載器調(diào)用findClass方法去找到該類,如果還是沒(méi)找到,就拋出異常
//然后讓應(yīng)用類加載器去找classpath下找該類
long t1 = System.nanoTime();
c = findClass(name);
// 記錄時(shí)間
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
有一個(gè)描述類加載器加載類過(guò)程的術(shù)語(yǔ):雙親委派模型。然而這是一個(gè)很有誤導(dǎo)性的術(shù)語(yǔ),它應(yīng)該叫做單親委派模型(Parent-Delegation Model)。但是沒(méi)有辦法,大家都已經(jīng)這樣叫了。所謂雙親委派,這個(gè)親就是指ClassLoader里的全局變量parent,也就是父加載器。
雙親委派的具體過(guò)程如下:
- 當(dāng)一個(gè)類加載器接收到類加載任務(wù)時(shí),先查緩存里有沒(méi)有,如果沒(méi)有,將任務(wù)委托給它的父加載器去執(zhí)行。
- 父加載器也做同樣的事情,一層一層往上委托,直到最頂層的啟動(dòng)類加載器為止。
- 如果啟動(dòng)類加載器沒(méi)有找到所需加載的類,便將此加載任務(wù)退回給下一級(jí)類加載器去執(zhí)行,而下一級(jí)的類加載器也做同樣的事情。
- 如果最底層類加載器仍然沒(méi)有找到所需要的class文件,則拋出異常。
- 所以是一條線傳上再傳下,并沒(méi)有什么“雙親”。

為什么要雙親委派?
答:確保類的全局唯一性。
如果你自己寫的一個(gè)類與核心類庫(kù)中的類重名,會(huì)發(fā)現(xiàn)這個(gè)類可以被正常編譯,但永遠(yuǎn)無(wú)法被加載運(yùn)行。因?yàn)槟銓懙倪@個(gè)類不會(huì)被應(yīng)用類加載器加載,而是被委托到頂層,被啟動(dòng)類加載器在核心類庫(kù)中找到了。如果沒(méi)有雙親委托機(jī)制來(lái)確保類的全局唯一性,誰(shuí)都可以編寫一個(gè)java.lang.Object類放在classpath下,那應(yīng)用程序就亂套了。
從安全的角度講,通過(guò)雙親委托機(jī)制,Java虛擬機(jī)總是先從最可信的Java核心API查找類型,可以防止不可信的類假扮被信任的類對(duì)系統(tǒng)造成危害。
5、自定義類加載器
如果我們自己去實(shí)現(xiàn)一個(gè)類加載器,基本上就是繼承ClassLoader之后重寫findClass方法,且在此方法的最后調(diào)包defineClass。
5.1、使用場(chǎng)景
- 想加載非 classpath 隨意路徑中的類文件
- 通過(guò)接口來(lái)使用實(shí)現(xiàn),希望解耦時(shí),常用在框架設(shè)計(jì)
- 這些類希望予以隔離,不同應(yīng)用的同名類都可以加載,不沖突,常見(jiàn)于 tomcat 容器
5.2、步驟
- 繼承ClassLoader父類
- 要遵從雙親委派機(jī)制,重寫 ?ndClass 方法
- 不是重寫loadClass方法,否則不會(huì)走雙親委派機(jī)制
- 讀取類文件的字節(jié)碼
- 調(diào)用父類的 de?neClass 方法來(lái)加載類
- 使用者調(diào)用該類加載器的 loadClass 方法
protected Class<?> findClass(final String name) throws ClassNotFoundException {
// 1、安全檢查
// 2、根據(jù)絕對(duì)路徑把硬盤上class文件讀入內(nèi)存
byte[] raw = getBytes(name);
// 3、將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成class對(duì)象
return defineClass(raw);
}
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
JDBC之PreparedStatement類中預(yù)編譯的綜合應(yīng)用解析
SQL 語(yǔ)句被預(yù)編譯并存儲(chǔ)在 PreparedStatement 對(duì)象中。然后可以使用此對(duì)象多次高效地執(zhí)行該語(yǔ)句2013-07-07
spring/springboot整合dubbo詳細(xì)教程
今天教大家如何使用spring/springboot整合dubbo,文中有非常詳細(xì)的圖文介紹及代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴有很好地幫助,需要的朋友可以參考下2021-05-05
Java中Easyexcel?實(shí)現(xiàn)批量插入圖片功能
這篇文章主要介紹了Easyexcel?實(shí)現(xiàn)批量插入圖片,本文通過(guò)實(shí)例代碼給大家介紹了easyexcel文檔處理工具、自定義圖片處理器的相關(guān)知識(shí),需要的朋友可以參考下2022-04-04

