虛擬機器執行子系統_類載入器、雙親委派模型

z1340954953發表於2018-04-17

類載入器的定義

通過一個類的全限定名來獲取描述此類的二進位制位元組流這個動作放到java虛擬機器外部實現,以便讓應用程式自己決定如何去獲取所需要的類。實現這個動作的程式碼稱為類載入器

類和類載入器

對於任意一個類,都需要由載入它的類載入器和這個類本身一同確定在java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。

也就是:比較兩個類是否相等,只有在這兩個類都是同一個類載入器載入的並且是來源於同一個Class檔案才會相等,否則,就算兩個類來源於同一個Class檔案,被同一個虛擬機器載入,只要載入它們的類載入器不同,那這兩個類就必定不相等

這裡的相等:包括類的Class物件的equals方法,isAssignableFrom,isInstance方法的返回結果。也會影響instanceof物件從屬關係的判斷

雙親委派模型

從java虛擬機器角度來看,存在兩種不同的累載入器:一種是啟動類載入器,另一種是所有其他的類載入器,這些類載入器都由java語言實現,獨立於虛擬機器外部,並且全部都繼承自抽象類java.lang.ClassLoader

java類載入器細分:

* 啟動類載入器(Bootstrap ClassLoader) : 負責將存放在<JAVA_HOME>\lib目錄中,或者被-Xbootclasspath引數指定的路徑中,並且是虛擬機器識別(僅按照檔名識別,如rt.jar,名字不符合的類庫)類庫載入到虛擬機器記憶體中。

啟動類載入器無法被java程式直接引用,使用者在編寫自定義類載入器時候,需要把載入請求委派給引導類載入器,那直接使用null代替就可

* 擴充套件類載入器(Extension ClassLoader): 這個載入器由sun.misc.Laucher$ExtClassLoader實現,負責載入<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變數所指定的路徑中的所有類庫,開發者可以直接使用擴充套件類載入器

檢視java.ext.dirs系統變數指定的路徑:

public static void main(String[] args) {
		System.out.println(System.getProperty("java.ext.dirs"));
	}

輸出: C:\Program Files\Java\jdk1.7.0_79\jre\lib\ext;C:\Windows\Sun\Java\lib\ext

*  應用程式類載入器(Application ClassLoader):這個類載入器有sun.misc.Launcher$App-ClassLoader實現。由於這個類載入器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱為系統類載入器,負責載入使用者類路徑ClassPath上的指定的類庫,開發者可用直接使用,一般情況下,這個載入器就是程式中預設的類載入器

public static void main(String[] args) {
		System.out.println(ClassLoader.getSystemClassLoader());
	}

輸出:sun.misc.Launcher$AppClassLoader@19b1de

驗證Application ClassLoader的父載入器是擴充套件類載入器

public static void main(String[] args) {
		System.out.println(ClassLoader.getSystemClassLoader().getParent());
	}

輸出:sun.misc.Launcher$ExtClassLoader@19b1de

再往上,擴充套件類載入器的父類應該是啟動類載入器

public static void main(String[] args) {
		System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
	}

結果輸出:null ,為什麼是null?

因為Application Classloader和Extension ClassLoader都是java實現的,在java堆中都存在一個例項,而啟動類載入器BootStrap ClassLoader是c實現的,在java堆沒有例項,所以列印為空

java程式一般都是由這三種類載入器互相配合載入的,可以加入自定義的類載入器,這些載入器的關係一般如圖:


圖中展示的類載入器之間的層次關係,稱為類載入器的雙親委派模型,這些類載入器除了頂層的沒有父類載入器,下面都存在父類載入器

雙親載入器的工作流程:

1. 如果一個類載入器接受到一個載入請求,它首先不會自己去載入這個類,而是把這個請求委派給父類載入器去載入,每一個層次的載入器都是如此

2. 只有父載入器無法完成這次載入(在它的搜尋範圍內,找不到這個類),才會給子類載入器去載入

雙親委派模型來組織類載入器使得類載入器之間具備了一種層次關係,不管去載入什麼類,都會優先去呼叫類載入器,比如java.lang.Object是在rt.jar中的,這樣不管在哪個環境中,載入器產生的Object.class都是同一個類,如果自己編寫一個Object類放在classpath下面,一個環境中存在兩個不同的Object.class,java程式最基本的行為無法保證,程式將一片混亂

即使重新定義rt.jar下的Object類,編譯可以通過,但是永遠不會去載入,即使自定義載入器載入也會拋異常


相關文章