預定義類載入器和雙親委派機制
-
JVM預定義的三種型別類載入器:
- 啟動(Bootstrap)類載入器:是用原生程式碼實現的類裝入器,它負責將
<Java_Runtime_Home>/lib
下面的類庫載入到記憶體中(比如rt.jar
)。由於引導類載入器涉及到虛擬機器本地實現細節,開發者無法直接獲取到啟動類載入器的引用,所以不允許直接通過引用進行操作。 - 標準擴充套件(Extension)類載入器:是由 Sun 的
ExtClassLoader(sun.misc.Launcher$ExtClassLoader)
實現的。它負責將< Java_Runtime_Home >/lib/ext
或者由系統變數java.ext.dir
指定位置中的類庫載入到記憶體中。開發者可以直接使用標準擴充套件類載入器。 - 系統(System)類載入器:是由 Sun 的
AppClassLoader(sun.misc.Launcher$AppClassLoader)
實現的。它負責將系統類路徑(CLASSPATH
)中指定的類庫載入到記憶體中。開發者可以直接使用系統類載入器。
除了以上列舉的三種類載入器,還有一種比較特殊的型別 — 執行緒上下文類載入器。
- 啟動(Bootstrap)類載入器:是用原生程式碼實現的類裝入器,它負責將
-
雙親委派機制描述
某個特定的類載入器在接到載入類的請求時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務時,才自己去載入。
幾點思考
-
Java虛擬機器的第一個類載入器是Bootstrap,這個載入器很特殊,它不是Java類,因此它不需要被別人載入,它巢狀在Java虛擬機器核心裡面,也就是JVM啟動的時候Bootstrap就已經啟動,它是用C++寫的二進位制程式碼(不是位元組碼),它可以去載入別的類。
這也是我們在測試時為什麼發現
System.class.getClassLoader()
結果為null的原因,這並不表示System這個類沒有類載入器,而是它的載入器比較特殊,是BootstrapClassLoader
,由於它不是Java類,因此獲得它的引用肯定返回null。 -
委託機制具體含義
當Java虛擬機器要載入一個類時,到底派出哪個類載入器去載入呢?- 首先當前執行緒的類載入器去載入執行緒中的第一個類(假設為類A)。
注:當前執行緒的類載入器可以通過Thread類的getContextClassLoader()獲得,也可以通過setContextClassLoader()自己設定類載入器。 - 如果類A中引用了類B,Java虛擬機器將使用載入類A的類載入器去載入類B。
- 還可以直接呼叫
ClassLoader.loadClass()
方法來指定某個類載入器去載入某個類。
- 首先當前執行緒的類載入器去載入執行緒中的第一個類(假設為類A)。
-
委託機制的意義 — 防止記憶體中出現多份同樣的位元組碼
比如兩個類A和類B都要載入System類:- 如果不用委託而是自己載入自己的,那麼類A就會載入一份System位元組碼,然後類B又會載入一份System位元組碼,這樣記憶體中就出現了兩份System位元組碼。
- 如果使用委託機制,會遞迴的向父類查詢,也就是首選用Bootstrap嘗試載入,如果找不到再向下。這裡的System就能在Bootstrap中找到然後載入,如果此時類B也要載入System,也從Bootstrap開始,此時Bootstrap發現已經載入過了System那麼直接返回記憶體中的System即可而不需要重新載入,這樣記憶體中就只有一份System的位元組碼了。
一道面試題
-
能不能自己寫個類叫
java.lang.System
?答案:通常不可以,但可以採取另類方法達到這個需求。
解釋:為了不讓我們寫System類,類載入採用委託機制,這樣可以保證爸爸們優先,爸爸們能找到的類,兒子就沒有機會載入。而System類是Bootstrap載入器載入的,就算自己重寫,也總是使用Java系統提供的System,自己寫的System類根本沒有機會得到載入。但是,我們可以自己定義一個類載入器來達到這個目的,為了避免雙親委託機制,這個類載入器也必須是特殊的。由於系統自帶的三個類載入器都載入特定目錄下的類,如果我們自己的類載入器放在一個特殊的目錄,那麼系統的載入器就無法載入,也就是最終還是由我們自己的載入器載入。