Java類加載器深入解析(二)

在做Java開發時瞭解Java類加載機制是非常好的。而對類加載機制的基本理解對Java開發人員處理類加載器(ClassLoader)相關的異常也很有幫助。

類加載器委託機制

Java類的裝載是通過類加載器(CL)來完成的,這些類加載器負責將類加載到JVM中。簡單的應用可以使用java平臺自帶的類加載器來加載自身的類,而稍微複雜一些的應用則傾向於自定義類加載來加載自身的類。

在java中類加載器是以樹狀結構組織的。通過請求一個類加載器並通過在其緩存中查找來確定某個類是否已被加載。如果在緩存中已經有該類,那麼CL直接返回;否則,該類加載器委託其父類加載器來加載該類。如果這個類加載器沒有父類或者父類不能加載這個類則會拋出ClassNotFoundException異常,此時被請求的類加載器會嘗試去在它自身的路徑下來查找並加載需要被加載類的類文件。如果找到了這個類,就將其返回;否則會拋出ClassNotFoundException異常。其中緩存的查找方式爲遞歸的從子類到父類查找,直到到達樹(譯註:類加載器已樹狀結構組織)的根節點或者在緩存中找到需要的類爲止。如果緩存搜索已到達根節點,則類加載器會從父類到子類再次展開遞歸來加載需要的類。總的來說順序如下:

1. 緩存

2. 父類

3. 子類

這種機制確保類會類會被最接近根結點的類加載器加載。請謹記,父類加載器總是有優先加載類的權限。而這很重要的作用就是確保核心java類都被bootstrap類加載器加載,這個類加載器是用來保證正確版本的類加載,如java.lang.Object。此外,bootstrap類加載器還保證一個類加載只能看到自身或其父類(或父類的父類等)加載的類,而不能看到其子類以及兄弟類(譯註:繼承同一個父類的類稱爲兄弟類)所加載的類。

下圖闡明瞭類加載器的層級關係。根加載器是bootstrap類加載,該加載器是通過native方法實現,並且不能在java代碼中實例化。

BootStrap之後便是擴展類加載器,該加載器的主要責任是從擴展目錄中加載類並且在新的JVM中不修改用戶classpath的情況下來提供簡單訪問JVM擴展的能力。而系統或者應用類加載器則負責從環境變量CLASSPATH中指定的路徑來加載類;這種類加載器可以通過調用ClassLoader.getSystemClassLoader()方法獲得。

類加載階段

具體的類加載可以分爲3個階段:物理加載,鏈接,初始化。

1 在物理加載階段,要求類可以在指定的classpath路徑中被找到。如果可以找到類文件,通過讀取類文件來加載字節碼。這個處理過程給出了單個類對象的一個基本內存結構,但是像方法、字段以及對以其他類的一些引用這些概念在這個階段是不知道的。

2 鏈接階段可以拆分爲3個主要階段,因爲這個階段比較複雜:

2.1 通過類加再起校驗字節碼,該過程會在字節碼上執行一系列校驗。

2.2 類準備。這個階段準備必要的數據結構,表示類中所定義的字段、方法以及所實現的接口。

2.3 解析某個特定類中所有的其他類引用。類可以通過很多途徑被應用,具體如下

2.3.1 父類

2.3.2 接口

2.3.3 字段類型

2.3.4 方法返回值類型

2.3.5 在方法中使用的本地變量類型

3 在初始化階段,這個類中包含的所有初始化塊都會被執行,一遍靜態變量都會被初始化爲默認值。

有趣的是類加載可以通過延遲加載機制來實現,因此一部分只是在第一次使用的時候才被加載而不是一開始就加載。

異常

在處理類加載問題時最大的挑戰在於問題很難在類加載這個處理過程體現出來,而是在之後使用這個類的時候纔會被體現出來。以下是類加載時兩個相關的異常以及造成對應異常的可能原因:

ClassNotFoundException

l 當被加載的類相關文件、目錄或者其他資源沒有添加到對應了類加載器或者父類加載器時

l 當類加載器的父類設置錯誤時

l 當類加載器使用錯誤時

NoClassDefFoundError

l 當被加載的類相關文件、目錄或者其他資源沒有添加到對應了類加載器或者父類加載器時

l 當類加載器的父類設置錯誤時

l 當一個類被加載時,該類中所包含的引用對該類的類加載器不可達時,例如加載器的子類

1. 本文由程序員學架構翻譯

2. 本文譯自 http://www.techjava.de/topics/2008/01/java-class-loading/

3. 轉載請務必註明本文出自程序員學架構(微信號:archleaner )

4. 更多文章請掃碼:



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章