Java虛擬機的類加載機制

duei7063 8年前發布 | 9K 次閱讀 Java開發

來自: http://blog.csdn.net//chenleixing/article/details/47099733


Java虛擬機類加載過程是把Class類文件加載到內存,并對Class文件中的數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的java類型的過程。

在加載階段,java虛擬機需要完成以下3件事:

a.通過一個類的全限定名來獲取定義此類的二進制字節流。

b.將定義類的二進制字節流所代表的靜態存儲結構轉換為方法區的運行時數據結構。

c.在java堆中生成一個代表該類的java.lang.Class對象,作為方法區數據的訪問入口。

Java虛擬機的類加載是通過類加載器實現的, Java中的類加載器體系結構如下:

(1).BootStrap ClassLoader:啟動類加載器,負責加載存放在%JAVA_HOME%\lib目錄中的,或者通被-Xbootclasspath參數所指定的路徑中的,并且被java虛擬機識別的(僅按照文件名識別,如rt.jar,名字不符合的類庫,即使放在指定路徑中也不會被加載)類庫到虛擬機的內存中,啟動類加載器無法被java程序直接引用。

(2).Extension ClassLoader:擴展類加載器,由sun.misc.Launcher$ExtClassLoader實現,負責加載%JAVA_HOME%\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器。

(3).Application ClassLoader:應用程序類加載器,由sun.misc.Launcher$AppClassLoader實現,負責加載用戶類路徑classpath上所指定的類庫,是類加載器ClassLoader中的getSystemClassLoader()方法的返回值,開發者可以直接使用應用程序類加載器,如果程序中沒有自定義過類加載器,該加載器就是程序中默認的類加載器。

注意:上述三個JDK提供的類加載器雖然是父子類加載器關系,但是沒有使用繼承,而是使用了組合關系。

從JDK1.2開始,java虛擬機規范推薦開發者使用雙親委派模式(ParentsDelegation Model)進行類加載,其加載過程如下:

(1).如果一個類加載器收到了類加載請求,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器去完成。

(2).每一層的類加載器都把類加載請求委派給父類加載器,直到所有的類加載請求都應該傳遞給頂層的啟動類加載器。

(3).如果頂層的啟動類加載器無法完成加載請求,子類加載器嘗試去加載,如果連最初發起類加載請求的類加載器也無法完成加載請求時,將會拋出ClassNotFoundException,而不再調用其子類加載器去進行類加載。

雙親委派 模式的類加載機制的優點是java類它的類加載器一起具備了一種帶優先級的層次關系,越是基礎的類,越是被上層的類加載器進行加載,保證了java程序的穩定運行。雙親委派模式的實現:
  1. protected synchronized Class<?> loadClass(String name, Boolean resolve) throws ClassNotFoundException{  
  2.     //首先檢查請求的類是否已經被加載過  
  3.     Class c = findLoadedClass(name);  
  4.     if(c == null){  
  5.     try{  
  6.         if(parent != null){//委派父類加載器加載  
  7.     c = parent.loadClass(name, false);  
  8. }  
  9. else{//委派啟動類加載器加載  
  10.     c = findBootstrapClassOrNull(name);   
  11. }  
  12. }catch(ClassNotFoundException e){  
  13.     //父類加載器無法完成類加載請求  
  14. }  
  15. if(c == null){//本身類加載器進行類加載  
  16.     c = findClass(name);  
  17. }  
  18. }  
  19. if(resolve){  
  20.     resolveClass(c);  
  21. }  
  22. return c;  
  23. }  
protected synchronized Class<?> loadClass(String name, Boolean resolve) throws ClassNotFoundException{
    //首先檢查請求的類是否已經被加載過
    Class c = findLoadedClass(name);
    if(c == null){
    try{
        if(parent != null){//委派父類加載器加載
    c = parent.loadClass(name, false);
}
else{//委派啟動類加載器加載
    c = findBootstrapClassOrNull(name); 
}
}catch(ClassNotFoundException e){
    //父類加載器無法完成類加載請求
}
if(c == null){//本身類加載器進行類加載
    c = findClass(name);
}
}
if(resolve){
    resolveClass(c);
}
return c;
}

若要實現自定義類加載器,只需要繼承java.lang.ClassLoader 類,并且重寫其findClass()方法即可。java.lang.ClassLoader 類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的字節代碼,然后從這些字節代碼中定義出一個 Java 類,即 java.lang.Class 類的一個實例。除此之外,ClassLoader 還負責加載 Java 應用所需的資源,如圖像文件和配置文件等,ClassLoader 中與加載類相關的方法如下:

 

方法

說明

getParent()

返回該類加載器的父類加載器。

loadClass(String name)

加載名稱為 二進制名稱為name 的類,返回的結果是 java.lang.Class 類的實例。

 

findClass(String name)

查找名稱為 name 的類,返回的結果是 java.lang.Class 類的實例。

 

findLoadedClass(String name)

查找名稱為 name 的已經被加載過的類,返回的結果是 java.lang.Class 類的實例。

 

resolveClass(Class<?> c)

鏈接指定的 Java 類。

注意:在JDK1.2之前,類加載尚未引入雙親委派模式,因此實現自定義類加載器時常常重寫loadClass方法,提供雙親委派邏輯,從JDK1.2之后,雙親委派模式已經被引入到類加載體系中,自定義類加載器時不需要在自己寫雙親委派的邏輯,因此不鼓勵重寫loadClass方法,而推薦重寫findClass方法。

在Java中,任意一個類都需要由加載它的類加載器和這個類本身一同確定其在java虛擬機中的唯一性,即比較兩個類是否相等,只有在這兩個類是由同一個類加載器加載的前提之下才有意義,否則,即使這兩個類來源于同一個Class類文件,只要加載它的類加載器不相同,那么這兩個類必定不相等(這里的相等包括代表類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()方法和instanceof關鍵字的結果)。例子代碼如下:

  1. package com.test;  
  2.   
  3. public class ClassLoaderTest {  
  4.     public static void main(String[] args)throws Exception{  
  5.         //匿名內部類實現自定義類加載器  
  6.     ClassLoader myClassLoader = new ClassLoader(){  
  7.     protected Class<?> findClass(String name)throws ClassNotFoundException{  
  8.         //獲取類文件名  
  9.     String filename = name.substring(name.lastIndexOf(“.”) + 1) + “.class”;  
  10.     InputStream in = getClass().getResourceAsStream(filename);  
  11.     if(in == null){  
  12.     throw RuntimeException(“Could not found class file:” + filename);  
  13. }  
  14. byte[] b = new byte[in.available()];  
  15. return defineClass(name, b, 0, b.length);  
  16. }catch(IOException e){  
  17.     throw new ClassNotFoundException(name);  
  18. }  
  19. };  
  20. Object obj = myClassLoader.loadClass(“com.test.ClassLoaderTest”).newInstance();  
  21. System.out.println(obj.getClass());  
  22. System.out.println(obj instanceof com.test. ClassLoaderTest);  
  23. }  
  24. }  
package com.test;

public class ClassLoaderTest {
    public static void main(String[] args)throws Exception{
        //匿名內部類實現自定義類加載器
    ClassLoader myClassLoader = new ClassLoader(){
    protected Class<?> findClass(String name)throws ClassNotFoundException{
        //獲取類文件名
    String filename = name.substring(name.lastIndexOf(“.”) + 1) + “.class”;
    InputStream in = getClass().getResourceAsStream(filename);
    if(in == null){
    throw RuntimeException(“Could not found class file:” + filename);
}
byte[] b = new byte[in.available()];
return defineClass(name, b, 0, b.length);
}catch(IOException e){
    throw new ClassNotFoundException(name);
}
};
Object obj = myClassLoader.loadClass(“com.test.ClassLoaderTest”).newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof com.test. ClassLoaderTest);
}
}

輸出結果如下:

com.test.ClassLoaderTest

false

之所以instanceof會返回false,是因為com.test.ClassLoaderTest類默認使用Application ClassLoader加載,而obj是通過自定義類加載器加載的,類加載不相同,因此不相等。

類加載器雙親委派模型是從JDK1.2以后引入的,并且只是一種推薦的模型,不是強制要求的,因此有一些沒有遵循雙親委派模型的特例:

(1).在JDK1.2之前,自定義類加載器都要覆蓋loadClass方法去實現加載類的功能,JDK1.2引入雙親委派模型之后,loadClass方法用于委派父類加載器進行類加載,只有父類加載器無法完成類加載請求時才調用自己的findClass方法進行類加載,因此在JDK1.2之前的類加載的loadClass方法沒有遵循雙親委派模型,因此在JDK1.2之后,自定義類加載器不推薦覆蓋loadClass方法,而只需要覆蓋findClass方法即可。

(2).雙親委派模式很好地解決了各個類加載器的基礎類統一問題,越基礎的類由越上層的類加載器進行加載,但是這個基礎類統一有一個不足,當基礎類想要調用回下層的用戶代碼時無法委派子類加載器進行類加載。為了解決這個問題JDK引入了ThreadContext線程上下文,通過線程上下文的setContextClassLoader方法可以設置線程上下文類加載器。

JavaEE只是一個規范,sun公司只給出了接口規范,具體的實現由各個廠商進行實現,因此JNDI,JDBC,JAXB等這些第三方的實現庫就可以被JDK的類庫所調用。線程上下文類加載器也沒有遵循雙親委派模型。

(3).近年來的熱碼替換,模塊熱部署等應用要求不用重啟java虛擬機就可以實現代碼模塊的即插即用,催生了OSGi技術,在OSGi中類加載器體系被發展為網狀結構。OSGi也沒有完全遵循雙親委派模型。

 本文由用戶 duei7063 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!