JVM加載class文件的原理機制(轉)

JVM加載class文件的原理機制

1.Java中的全部類,必須被裝載到jvm中才能運行,這個裝載工做是由jvm中的類裝載器完成的,類裝載器所作的工做實質是把類文件從硬盤讀取到內存中 

2.java中的類大體分爲三種: 
    1.系統類 
    2.擴展類 
    3.由程序員自定義的類 

3.類裝載方式,有兩種 
    1.隱式裝載, 程序在運行過程當中當碰到經過new 等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中, 
    2.顯式裝載, 經過class.forname()等方法,顯式加載須要的類 
  隱式加載與顯式加載的區別: 
    二者本質是同樣?, 

4.類加載的動態性體現 
    一個應用程序老是由n多個類組成,Java程序啓動時,並非一次把全部的類所有加載後再 
運行,它老是先把保證程序運行的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載,這樣的好處是節省了內存的開銷,由於java最先就是爲嵌入式系統而設計的,內存寶貴,這是一種能夠理解的機制,而用到時再加載這也是java動態性的一種體現 

5.java類裝載器 
    Java中的類裝載器實質上也是類,功能是把類載入jvm中,值得注意的是jvm的類裝載器並非一個,而是三個,層次結構以下: 
      Bootstrap Loader  - 負責加載系統類 
            | 
          - - ExtClassLoader  - 負責加載擴展類 
                          | 
                      - - AppClassLoader  - 負責加載應用類 
        爲何要有三個類加載器,一方面是分工,各自負責各自的區塊,另外一方面爲了實現委託模型,下面會談到該模型 

6. 類加載器之間是如何協調工做的 
      前面說了,java中有三個類加載器,問題就來了,碰到一個類須要加載時,它們之間是如何協調工做的,即java是如何區分一個類該由哪一個類加載器來完成呢。 
在這裏java採用了委託模型機制,這個機制簡單來說,就是「類裝載器有載入類的需求時,會先請示其Parent使用其搜索路徑幫忙載入,若是Parent 找不到,那麼才由本身依照本身的搜索路徑搜索類」,注意喔,這句話具備遞歸性 

java

Java代碼   收藏代碼
  1. /** 
  2.  * @author Jamson Huang 
  3.  * 
  4.  */  
  5. public class TestClass {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args)  throws Exception{  
  11.         //調用class加載器  
  12.         ClassLoader cl = TestClass.class.getClassLoader();  
  13.         System.out.println(cl);  
  14.         //調用上一層Class加載器  
  15.         ClassLoader clParent = cl.getParent();  
  16.         System.out.println(clParent);  
  17.         //調用根部Class加載器  
  18.         ClassLoader clRoot = clParent.getParent();  
  19.         System.out.println(clRoot);  
  20.           
  21.     }  
  22.   
  23. }  

 

Result代碼   收藏代碼
  1. Run, Console中出現的log信息以下:  
  2. sun.misc.Launcher$AppClassLoader@7259da  
  3. sun.misc.Launcher$ExtClassLoader@16930e2  
  4. null  


能夠看出TestClass是由AppClassLoader加載器加載的 
AppClassLoader的Parent 加載器是 ExtClassLoader 
可是ExtClassLoader的Parent爲 null 是怎麼回事呵,朋友們留意的話,前面有提到Bootstrap Loader是用C++語言寫的,依java的觀點來看,邏輯上並不存在Bootstrap Loader的類實體,因此在java程序代碼裏試圖打印出其內容時,咱們就會看到輸出爲null 
【注:如下內容大部分引用java深度歷險】 
弄明白了上面的示例,接下來直接進入類裝載的委託模型實例,寫兩個文件,以下:程序員

Java代碼   收藏代碼
  1. /** 
  2.  * @author Jamson Huang 
  3.  * 
  4.  */  
  5. public class Test1 {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args)throws Exception {  
  11.         System.out.println(Test1.class.getClassLoader());  
  12.           
  13.         Test2 test2 = new Test2();  
  14.           
  15.         test2.print();  
  16.     }  
  17.   
  18. }  
  19. /** 
  20.  * @author Jamson Huang 
  21.  * 
  22.  */  
  23. public class Test2 {  
  24.     public void print(){  
  25.         System.out.println(Test2.class);  
  26.         System.out.println(this.getClass());  
  27.         System.out.println(Test2.class.getClassLoader());  
  28.     }  
  29. }  
Result代碼   收藏代碼
  1. Run,Console出現log以下:  
  2. sun.misc.Launcher$AppClassLoader@7259da  
  3. class com.java.test.Test2  
  4. class com.java.test.Test2  
  5. sun.misc.Launcher$AppClassLoader@7259da  


7. 預先加載與依需求加載 

Java 運行環境爲了優化系統,提升程序的執行速度,在 JRE 運行的開始會將 Java 運行所須要的基本類採用預先加載( pre-loading )的方法所有加載要內存當中,由於這些單元在 Java 程序運行的過程中常常要使用的,主要包括 JRE 的 rt.jar 文件裏面全部的 .class 文件。 

當 java.exe 虛擬機開始運行之後,它會找到安裝在機器上的 JRE 環境,而後把控制權交給 JRE , JRE 的類加載器會將 lib 目錄下的 rt.jar 基礎類別文件庫加載進內存,這些文件是 Java 程序執行所必須的,因此係統在開始就將這些文件加載,避免之後的屢次 IO 操做,從而提升程序執行效率。 

圖( 2 )咱們能夠看到多個基礎類被加載, java.lang.Object,java.io.Serializable 等等。
相對於預先加載,咱們在程序中須要使用本身定義的類的時候就要使用依需求加載方法( load-on-demand ),就是在 Java 程序須要用到的時候再加載,以減小內存的消耗,由於 Java 語言的設計初衷就是面向嵌入式領域的。
8. 自定義類加載機制 

以前咱們都是調用系統的類加載器來實現加載的,其實咱們是能夠本身定義類加載器的。利用 Java 提供的 java.net.URLClassLoader 類就能夠實現。下面咱們看一段範例: 

編程

Java代碼   收藏代碼
  1. try{   
  2. URL url = new URL("file:/d:/test/lib/");   
  3. URLClassLoader urlCL = new URLClassLoader(new URL[]{url});   
  4. Class c = urlCL.loadClass("TestClassA");   
  5. TestClassA object = (TestClassA)c.newInstance();   
  6. object.method();   
  7. }catch(Exception e){   
  8. e.printStackTrace();   
  9. }   



咱們經過自定義的類加載器實現了 TestClassA 類的加載並調用 method ()方法。分析一下這個程序:首先定義 URL 指定類加載器從何處加載類, URL 能夠指向網際網絡上的任何位置,也能夠指向咱們計算機裏的文件系統 ( 包含 JAR 文件 ) 。上述範例當中咱們從 file:/d:/test/lib/ 處尋找類;而後定義 URLClassLoader 來加載所需的類,最後便可使用該實例了。 

9. 類加載器的階層體系 

討論了這麼多之後,接下來咱們仔細研究一下 Java 的類加載器的工做原理: 

當執行 java ***.class 的時候, java.exe 會幫助咱們找到 JRE ,接着找到位於 JRE 內部的 jvm.dll ,這纔是真正的 Java 虛擬機器 , 最後加載動態庫,激活 Java 虛擬機器。虛擬機器激活之後,會先作一些初始化的動做,好比說讀取系統參數等。一旦初始化動做完成以後,就會產生第一個類加載器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰寫而成,這個 Bootstrap Loader 所作的初始工做中,除了一些基本的初始化動做以外,最重要的就是加載 Launcher.java 之中的 ExtClassLoader ,並設定其 Parent 爲 null ,表明其父加載器爲 BootstrapLoader 。而後 Bootstrap Loader 再要求加載 Launcher.java 之中的 AppClassLoader ,並設定其 Parent 爲以前產生的 ExtClassLoader 實體。這兩個加載器都是以靜態類的形式存在的。這裏要請你們注意的是, Launcher$ExtClassLoader.class 與 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加載,因此 Parent 和由哪一個類加載器加載沒有關係。 

下面的圖形能夠表示三者之間的關係: 
BootstrapLoader <---(Extends)----AppClassLoader <---(Extends)----ExtClassLoader 

這三個加載器就構成咱們的 Java 類加載體系。他們分別從如下的路徑尋找程序所須要的類: 

BootstrapLoader : sun.boot.class.path 
ExtClassLoader: java.ext.dirs 
AppClassLoader: java.class.path 

這三個系統參量能夠經過 System.getProperty() 函數獲得具體對應的路徑。你們能夠本身編程實現查看具體的路徑。網絡

 

轉自:http://blog.csdn.net/seelye/article/details/8266192jvm

相關文章
相關標籤/搜索