JVM類加載之雙親委派機(jī)制解讀
前言
在面試過(guò)程中:也會(huì)被問(wèn)到關(guān)于如何理解雙親委派模型這樣的問(wèn)題,接下來(lái)就通過(guò)這篇文章往明白了解一下。
我們首先需要了解一下類加載階段
類的加載階段
類加載階段分為加載、連接、初始化三個(gè)階段,而加載階段需要通過(guò)類的全限定名來(lái)獲取定義了此類的二進(jìn)制字節(jié)流。Java特意把這一步抽出來(lái)用類加載器來(lái)實(shí)現(xiàn)。把這一步驟抽離出來(lái)使得應(yīng)用程序可以按需自定義類加載器。并且得益于類加載器,OSGI、熱部署等領(lǐng)域才得以在JAVA中得到應(yīng)用。
類加載器除了能用來(lái)加載類,還能用來(lái)作為類的層次劃分。Java自身提供了3種類加載器
類加載器
1、啟動(dòng)類加載器(Bootstrap ClassLoader),它是屬于虛擬機(jī)自身的一部分,用C++實(shí)現(xiàn)的,主要負(fù)責(zé)加載<JAVA_HOME>\lib目錄中或被-Xbootclasspath指定的路徑中的并且文件名是被虛擬機(jī)識(shí)別的文件。它等于是所有類加載器的爸爸。
2、擴(kuò)展類加載器(Extension ClassLoader),它是Java實(shí)現(xiàn)的,獨(dú)立于虛擬機(jī),主要負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中或被java.ext.dirs系統(tǒng)變量所指定的路徑的類庫(kù)。
3、應(yīng)用程序類加載器(Application ClassLoader),它是Java實(shí)現(xiàn)的,獨(dú)立于虛擬機(jī)。主要負(fù)責(zé)加載用戶類路徑(classPath)上的類庫(kù),如果我們沒(méi)有實(shí)現(xiàn)自定義的類加載器那這玩意就是我們程序中的默認(rèn)加載器。
什么是雙親委派機(jī)制
上圖:
簡(jiǎn)單來(lái)說(shuō):如果一個(gè)類加載器需要加載類,那么首先它會(huì)把這個(gè)類請(qǐng)求委派給父類加載器去完成,每一層都是如此。一直遞歸到頂層,當(dāng)父加載器無(wú)法完成這個(gè)請(qǐng)求時(shí),子類才會(huì)嘗試去加載。這里的雙親其實(shí)就指的是父類,沒(méi)有mother。父類也不是我們平日所說(shuō)的那種繼承關(guān)系,只是調(diào)用邏輯是這樣。
也就是當(dāng)類加載器收到類加載的請(qǐng)求時(shí)候會(huì)首先調(diào)用父類(ClassLoader)的findLoadedClass方法,判斷類是否加載過(guò),如果已經(jīng)加載過(guò)則直接返回,否則會(huì)將加載任務(wù)委托給成員變量parent,并不是指的父類。parent加載器在收到類加載請(qǐng)求后,也會(huì)先判斷需要加載的類是否已經(jīng)加載過(guò),如果加載過(guò)則結(jié)束,否則也會(huì)將加載任務(wù)委托給成員變量parent去進(jìn)行類加載。這里是一個(gè)循環(huán)過(guò)程,直到將加載任務(wù)委托給Bootstrap ClassLoader 結(jié)束,如果Bootstrap ClassLoader也沒(méi)有找到則交給各個(gè)子類自己加載,一直到最后,如果沒(méi)有任何類加載器能加載則會(huì)拋出ClassNotFoundException。
為什么要設(shè)計(jì)雙親委派機(jī)制?
- 沙箱安全機(jī)制
自己寫(xiě)的java.lang.String.class類不會(huì)被加載,這樣可以防止核心API庫(kù)被隨意篡改。 為了不讓我們寫(xiě)String類,類加載采用委托機(jī)制,這樣可以保證爸爸們優(yōu)先,爸爸們能找到的類,兒子就沒(méi)有機(jī)會(huì)加載。而String類是Bootstrap加載器加載的,就算自己重寫(xiě),也總是使用Java系統(tǒng)提供的String,自己寫(xiě)的String類根本沒(méi)有機(jī)會(huì)得到加載。
- 避免類的重復(fù)加載
當(dāng)父親已經(jīng)加載了該類, 就沒(méi)必要子classLoader再加載一次,保證被加載的唯一性。
引申內(nèi)容
JVM 類加載器和類本身一同確立類在Java虛擬機(jī)中的唯一性
問(wèn)題:由不同類加載器加載同一個(gè)類,實(shí)例化為對(duì)象。使用instanceof判斷該對(duì)象與該類的歸屬,請(qǐng)問(wèn)結(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
原因
對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立其在Java虛擬機(jī)中的唯一性,每一個(gè)類加載器,都擁有一個(gè)獨(dú)立的類名稱空間。這句話可以表達(dá)得更通俗一些:比較兩個(gè)類是否“相等”,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義,否則,即使這兩個(gè)類來(lái)源于同一個(gè)Class文件,被同一個(gè)虛擬機(jī)加載,只要加載它們的類加載器不同,那這兩個(gè)類就必定不相等。
結(jié)果分析
兩行輸出結(jié)果中,從第一句可以看出,這個(gè)對(duì)象確實(shí)是類Practice.Java.ClassLoaderTest實(shí)例化出來(lái)的對(duì)象,但從第二句可以發(fā)現(xiàn),這個(gè)對(duì)象與類Practice.Java.ClassLoaderTest做所屬類型檢查的時(shí)候卻返回了false。
這是因?yàn)樘摂M機(jī)中存在了兩個(gè)ClassLoaderTest類,一個(gè)是由系統(tǒng)應(yīng)用程序類加載器加載的,另外一個(gè)是由我們自定義的類加載器加載的。雖然都來(lái)自同一個(gè)Class文件,但依然是兩個(gè)獨(dú)立的類,做對(duì)象所屬類型檢查時(shí)結(jié)果自然為false。
結(jié)論
Java類加載器這種特性可以簡(jiǎn)單的總結(jié)為命名空間。
即在 Java 虛擬機(jī)中,類的唯一性是由類加載器實(shí)例以及類的全名一同確定的。即便是同一串字節(jié)流,經(jīng)由不同的類加載器加載,也會(huì)得到兩個(gè)不同的類。
到此這篇關(guān)于JVM類加載之雙親委派機(jī)制解讀的文章就介紹到這了,更多相關(guān)JVM雙親委派機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java8?stream流分組groupingBy的使用方法代碼
對(duì)于java8的新特性groupingBy方法,相信有很多人都在工作中用過(guò),這篇文章主要給大家介紹了關(guān)于Java8?stream流分組groupingBy的使用方法,需要的朋友可以參考下2024-01-01SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能
這篇文章主要為大家詳細(xì)介紹了SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03springBoot Junit測(cè)試用例出現(xiàn)@Autowired不生效的解決
這篇文章主要介紹了springBoot Junit測(cè)試用例出現(xiàn)@Autowired不生效的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09JUnit中獲取測(cè)試類及方法的名稱實(shí)現(xiàn)方法
這篇文章主要介紹了JUnit中獲取測(cè)試類及方法的名稱實(shí)現(xiàn)方法,本文使用了JUnit中提供的TestName實(shí)現(xiàn),不過(guò)還有一些編程細(xì)節(jié)需要注意,需要的朋友可以參考下2015-06-06Spring運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)bean的方法
這篇文章主要介紹了Spring運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)bean的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08MyBatis快速入門(mén)之環(huán)境搭建和單表映射
一說(shuō)起對(duì)象關(guān)系映射框架,大家第一時(shí)間想到的肯定是Hibernate。Hibernate作為一個(gè)著名的框架,功能十分強(qiáng)大。但是由于Hibernate如此強(qiáng)大的功能,導(dǎo)致了它的缺點(diǎn)。好吧,不多說(shuō)了,具體詳情大家通過(guò)本文一起學(xué)習(xí)吧2017-03-03mybatis中關(guān)于mapper的使用以及注意事項(xiàng)
這篇文章主要介紹了mybatis中關(guān)于mapper的使用以及注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06