JVM類加載之雙親委派機(jī)制解讀
前言
在面試過程中:也會被問到關(guān)于如何理解雙親委派模型這樣的問題,接下來就通過這篇文章往明白了解一下。
我們首先需要了解一下類加載階段
類的加載階段
類加載階段分為加載、連接、初始化三個階段,而加載階段需要通過類的全限定名來獲取定義了此類的二進(jìn)制字節(jié)流。Java特意把這一步抽出來用類加載器來實(shí)現(xiàn)。把這一步驟抽離出來使得應(yīng)用程序可以按需自定義類加載器。并且得益于類加載器,OSGI、熱部署等領(lǐng)域才得以在JAVA中得到應(yīng)用。
類加載器除了能用來加載類,還能用來作為類的層次劃分。Java自身提供了3種類加載器
類加載器
1、啟動類加載器(Bootstrap ClassLoader),它是屬于虛擬機(jī)自身的一部分,用C++實(shí)現(xiàn)的,主要負(fù)責(zé)加載<JAVA_HOME>\lib目錄中或被-Xbootclasspath指定的路徑中的并且文件名是被虛擬機(jī)識別的文件。它等于是所有類加載器的爸爸。
2、擴(kuò)展類加載器(Extension ClassLoader),它是Java實(shí)現(xiàn)的,獨(dú)立于虛擬機(jī),主要負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中或被java.ext.dirs系統(tǒng)變量所指定的路徑的類庫。
3、應(yīng)用程序類加載器(Application ClassLoader),它是Java實(shí)現(xiàn)的,獨(dú)立于虛擬機(jī)。主要負(fù)責(zé)加載用戶類路徑(classPath)上的類庫,如果我們沒有實(shí)現(xiàn)自定義的類加載器那這玩意就是我們程序中的默認(rèn)加載器。
什么是雙親委派機(jī)制
上圖:

簡單來說:如果一個類加載器需要加載類,那么首先它會把這個類請求委派給父類加載器去完成,每一層都是如此。一直遞歸到頂層,當(dāng)父加載器無法完成這個請求時,子類才會嘗試去加載。這里的雙親其實(shí)就指的是父類,沒有mother。父類也不是我們平日所說的那種繼承關(guān)系,只是調(diào)用邏輯是這樣。
也就是當(dāng)類加載器收到類加載的請求時候會首先調(diào)用父類(ClassLoader)的findLoadedClass方法,判斷類是否加載過,如果已經(jīng)加載過則直接返回,否則會將加載任務(wù)委托給成員變量parent,并不是指的父類。parent加載器在收到類加載請求后,也會先判斷需要加載的類是否已經(jīng)加載過,如果加載過則結(jié)束,否則也會將加載任務(wù)委托給成員變量parent去進(jìn)行類加載。這里是一個循環(huán)過程,直到將加載任務(wù)委托給Bootstrap ClassLoader 結(jié)束,如果Bootstrap ClassLoader也沒有找到則交給各個子類自己加載,一直到最后,如果沒有任何類加載器能加載則會拋出ClassNotFoundException。
為什么要設(shè)計(jì)雙親委派機(jī)制?
- 沙箱安全機(jī)制
自己寫的java.lang.String.class類不會被加載,這樣可以防止核心API庫被隨意篡改。 為了不讓我們寫String類,類加載采用委托機(jī)制,這樣可以保證爸爸們優(yōu)先,爸爸們能找到的類,兒子就沒有機(jī)會加載。而String類是Bootstrap加載器加載的,就算自己重寫,也總是使用Java系統(tǒng)提供的String,自己寫的String類根本沒有機(jī)會得到加載。
- 避免類的重復(fù)加載
當(dāng)父親已經(jīng)加載了該類, 就沒必要子classLoader再加載一次,保證被加載的唯一性。
引申內(nèi)容
JVM 類加載器和類本身一同確立類在Java虛擬機(jī)中的唯一性
問題:由不同類加載器加載同一個類,實(shí)例化為對象。使用instanceof判斷該對象與該類的歸屬,請問結(jié)果是true還是false?
答案是false。
驗(yàn)證解析
import java.io.IOException;
import java.io.InputStream;
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
Object obj = myLoader.loadClass("Practice.Java.ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof Practice.Java.ClassLoaderTest);
}
}輸出結(jié)果:
class Practice.Java.ClassLoaderTest
false
原因
對于任意一個類,都需要由加載它的類加載器和這個類本身一同確立其在Java虛擬機(jī)中的唯一性,每一個類加載器,都擁有一個獨(dú)立的類名稱空間。這句話可以表達(dá)得更通俗一些:比較兩個類是否“相等”,只有在這兩個類是由同一個類加載器加載的前提下才有意義,否則,即使這兩個類來源于同一個Class文件,被同一個虛擬機(jī)加載,只要加載它們的類加載器不同,那這兩個類就必定不相等。
結(jié)果分析
兩行輸出結(jié)果中,從第一句可以看出,這個對象確實(shí)是類Practice.Java.ClassLoaderTest實(shí)例化出來的對象,但從第二句可以發(fā)現(xiàn),這個對象與類Practice.Java.ClassLoaderTest做所屬類型檢查的時候卻返回了false。
這是因?yàn)樘摂M機(jī)中存在了兩個ClassLoaderTest類,一個是由系統(tǒng)應(yīng)用程序類加載器加載的,另外一個是由我們自定義的類加載器加載的。雖然都來自同一個Class文件,但依然是兩個獨(dú)立的類,做對象所屬類型檢查時結(jié)果自然為false。
結(jié)論
Java類加載器這種特性可以簡單的總結(jié)為命名空間。
即在 Java 虛擬機(jī)中,類的唯一性是由類加載器實(shí)例以及類的全名一同確定的。即便是同一串字節(jié)流,經(jīng)由不同的類加載器加載,也會得到兩個不同的類。
到此這篇關(guān)于JVM類加載之雙親委派機(jī)制解讀的文章就介紹到這了,更多相關(guān)JVM雙親委派機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java8?stream流分組groupingBy的使用方法代碼
對于java8的新特性groupingBy方法,相信有很多人都在工作中用過,這篇文章主要給大家介紹了關(guān)于Java8?stream流分組groupingBy的使用方法,需要的朋友可以參考下2024-01-01
SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能
這篇文章主要為大家詳細(xì)介紹了SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
springBoot Junit測試用例出現(xiàn)@Autowired不生效的解決
這篇文章主要介紹了springBoot Junit測試用例出現(xiàn)@Autowired不生效的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
JUnit中獲取測試類及方法的名稱實(shí)現(xiàn)方法
這篇文章主要介紹了JUnit中獲取測試類及方法的名稱實(shí)現(xiàn)方法,本文使用了JUnit中提供的TestName實(shí)現(xiàn),不過還有一些編程細(xì)節(jié)需要注意,需要的朋友可以參考下2015-06-06
Spring運(yùn)行時動態(tài)注冊bean的方法
這篇文章主要介紹了Spring運(yùn)行時動態(tài)注冊bean的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
mybatis中關(guān)于mapper的使用以及注意事項(xiàng)
這篇文章主要介紹了mybatis中關(guān)于mapper的使用以及注意事項(xiàng),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06

