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