類加載機制

類加載器層次是怎麼樣的

  1. 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)中指定的類庫加載到內存中。開發者可以直接使用系統類加載器。

    除了以上列舉的三種類加載器,還有一種比較特殊的類型 — 線程上下文類加載器。

雙親委派模型是什麼?有什麼好處?

某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。

委託機制的意義 — 防止內存中出現多份同樣的字節碼 
比如兩個類A和類B都要加載System類:

  • 如果不用委託而是自己加載自己的,那麼類A就會加載一份System字節碼,然後類B又會加載一份System字節碼,這樣內存中就出現了兩份System字節碼。
  • 如果使用委託機制,會遞歸的向父類查找,也就是首選用Bootstrap嘗試加載,如果找不到再向下。這裏的System就能在Bootstrap中找到然後加載,如果此時類B也要加載System,也從Bootstrap開始,此時Bootstrap發現已經加載過了System那麼直接返回內存中的System即可而不需要重新加載,這樣內存中就只有一份System的字節碼了。

當Java虛擬機要加載一個類時,到底派出哪個類加載器去加載呢?


  • 首先當前線程的類加載器去加載線程中的第一個類(假設爲類A)。 
    注:當前線程的類加載器可以通過Thread類的getContextClassLoader()獲得,也可以通過setContextClassLoader()自己設置類加載器。
  • 如果類A中引用了類B,Java虛擬機將使用加載類A的類加載器去加載類B。
  • 還可以直接調用ClassLoader.loadClass()方法來指定某個類加載器去加載某個類。

如果定義自己的符合雙親委派模型的加載器類?

繼承java.lang.ClassLoader,並重新findClass方法,以指定如何找到並加載class字節流,最早的自定義加載器是通過重載loadClass實現的,爲了符合雙親委派模型,一般不建議再重寫此方法。


獲取加載器的方法總結

獲得系統類加載器

複製代碼
public class testClassLoader {
    @Test
    public void test(){
        //application class loader
        System.out.println(ClassLoader.getSystemClassLoader());
        //extensions class loader
        System.out.println(ClassLoader.getSystemClassLoader().getParent());
        //bootstrap class loader
        System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
    }
}
複製代碼

輸出爲:

 

可以看出ClassLoader類是由AppClassLoader加載的。他的父親是ExtClassLoader,ExtClassLoader的父親無法獲取是因爲它是用C++實現的。

 獲得指定類的加載器

Lotto.class.getClassLoader();

加載器常用方法總結

方法 說明
getParent() 返回該類加載器的父類加載器
loadClass(String name) 加載名稱爲name的類,返回結果是一個java.lang.Class實例
findClass(String name) 查找名稱爲name的類,返回結果是一個java.lang.Class實例
findLoadedClass(String name) 查找名稱爲name的已經被加載的類,返回結果是一個java.lang.Class實例
defineClass(String name, byte[] b, int off, int len) 把字節數組b中的內容轉換爲一個java.lang.Class實例,該方法是final的
resolveClass(Class<?> c) 鏈接指定的class

一般來說,如果需要實現自己的類加載器,只需要實現findClass方法即可,loadClass封裝了下面提到的委託機制。


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