JVM五.類加載器

博主最近復習深入理解JVM一書,整理歸納,以形成系統認識和方便日后復習。
本文主要介紹

  1. 類加載器分類
  2. 雙親委派模型

總覽

定義

虛擬機設計團隊把類加載階段中的“通過一個類的全限定名來獲取描述此類的二進制字節流”這個動作放到Java虛擬機外部去實現,以便讓程序自己決定如何獲取所需的類。實現這個動作的代碼模塊稱為類加載器

類與類加載器

Java中的任意一個類的唯一性,都需要加載他的類加載器和這個類本身一同確定。,每一個類加載器,都擁有一個獨立的類名稱空間。

換句話說,判斷兩個類是否“相等”,只有在這兩個類都是由同一個類加載器加載的前提下才有意義。否則,即使類的來源是同一個Class文件,被同一個虛擬機加載,只要加載他們的類加載器不同,那這兩個類就必定不相等。

類加載器分類

  • 從Java虛擬機的角度來講,只存在兩種不同的類加載器:
    一種是啟動類加載器(Bootstrap ClassLoader),這個類加載器使用C++語言實現,是虛擬機自身的一部分
    另外一種就是其他的類加載器,這些類加載器都由Java語言實現,獨立于虛擬機外部,并且都繼承自抽象java.lang.ClassLoader。

  • 在Java開發人員的角度來看,類加載器可以大致劃分為以下三類:

啟動類加載器:Bootstrap ClassLoader

  • 負責加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數指定的路徑中的
  • 能被虛擬機識別的類庫
    • 如rt.jar,所有的java.開頭的類均被Bootstrap ClassLoader加載
    • 名字不符合的類庫即使放在 lib 目錄中也不會被加載
  • 啟動類加載器無法被 Java 程序直接引用,用戶在編寫自定義類加載器時,如果需要把加載請求委派給啟動類加載器,直接使用 null 代替即可。

擴展類加載器:Extension ClassLoader

該加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載JDK\jre\lib\ext目錄中,或者由java.ext.dirs系統變量指定的路徑中的所有類庫(如javax.開頭的類),開發者可以直接使用擴展類加載器。

應用程序類加載器:Application ClassLoader

該類加載器由sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。

一個小例子

public class ClassLoaderTest {
    public static void main(String[] args) {
       ClassLoader loader = Thread.currentThread().getContextClassLoader();
       System.out.println(loader);
       System.out.println(loader.getParent());
       System.out.println(loader.getParent().getParent());
   }

運行后,輸出結果:

類加載器結果

從上面的結果可以看出,并沒有獲取到ExtClassLoader的父Loader,原因是Bootstrap Loader(引導類加載器)是用C++語言實現的,找不到一個確定的返回父Loader的方式,于是就返回null。

雙親委派模式(Parents Delegation Model)

應用程序都是由三種類加載器相互配合進行加載的,如果有必要,還可以加入自己定義的類加載器。JVM在加載類時默認采用的是雙親委派機制

定義

類加載器間的關系

通俗的講,就是

  1. 某個特定的類加載器在接到加載類的請求時,不會首先嘗試加載這個類,而是將加載任務委托給父類加載器
  2. 每一層的類加載器都是如此。依次遞歸 (本質上就是loadClass函數的遞歸調用)。因此,所有的加載請求最終都應該傳送到頂層的啟動類加載器中。
  3. 如果父類加載器可以完成這個類加載請求,就成功返回;只有當父類加載器無法完成此加載請求時,子加載器才會嘗試自己去加載

事實上,大多數情況下,越基礎的類由越上層的加載器進行加載,因為這些基礎類之所以稱為“基礎”,是因為它們總是作為被用戶代碼調用的API(當然,也存在基礎類回調用戶用戶代碼的情形)。

雙親委派模式要求除了頂層的啟動類加載器外,其余的類加載器都應當有自己的父類加載器,雙親委派模式中的父子關系并非通常所說的類繼承關系,而是采用組合關系來復用父類加載器的相關代碼

雙親委派模式優勢

防止內存中出現多份同樣的字節碼

采用雙親委派模式的是好處是Java類隨著它的類加載器一起具備了一種帶有優先級的層次關系,通過這種層級關可以避免類的重復加載,當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次。

保證Java程序安全穩定運行

其次是考慮到安全因素,java核心api中定義類型不會被隨意替換,假設通過網絡傳遞一個名為java.lang.Integer的類,通過雙親委托模式傳遞到啟動類加載器,而啟動類加載器在核心Java API發現這個名字的類,發現該類已被加載,并不會重新加載網絡傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,這樣便可以防止核心API庫被隨意篡改。

參考文章
周志明,深入理解Java虛擬機:JVM高級特性與最佳實踐,機械工業出版社
深入理解Java類加載器(ClassLoader)
jvm系列(一):java類的加載機制

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容