JVM加載class文件的原理機制


1.Java中的所有類,必須被裝載到jvm中才能運行,這個裝載工作是由jvm中的類裝載器完成的

    類裝載器所做的工作實質是把類文件從硬盤讀取到內存中

2.java中的類大致分為三種: 

    1.系統類 

    2.擴展類 

    3.由程序員自定義的類

3.類裝載方式,有兩種 

    1.隱式裝載, 程序在運行過程中當碰到通過new 等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中, 

    2.顯式裝載, 通過class.forname()等方法,顯式加載需要的類 
     隱式加載與顯式加載的區別: 兩者本質是一樣的!都是加載到JVM虛擬機中。

4.類加載的動態性體現 

        一個應用程序總是由n多個類組成,Java程序啟動時,並不是一次把所有的類全部加載后再 

運行,它總是先把保證程序運行的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載,這樣的好處是節省了內存的開銷,因為java最早就是為嵌入式系統而設計的,內存寶貴,這是一種可以理解的機制,而用到時再加載這也是java動態性的一種體現

 

5.java類裝載器 

    Java中的類裝載器實質上也是類,功能是把類載入jvm中,值得注意的是jvm的類裝載器並不是一個,而是三個,層次結構如下: 

      Bootstrap Loader  - 負責加載系統類 

            | 

          - - ExtClassLoader  - 負責加載擴展類 

                          | 

                      - - AppClassLoader  - 負責加載應用類 

        為什么要有三個類加載器,一方面是分工,各自負責各自的區塊,另一方面為了實現委托模型,下面會談到該模型

 

6. 類加載器之間是如何協調工作的 

   前面說了,java中有三個類加載器,問題就來了,碰到一個類需要加載時,它們之間是如何協調工作的,即java是如何區分一個類該由哪個類加載器來完成呢。 

        在這里java采用了委托模型機制,這個機制簡單來講,就是“類裝載器有載入類的需求時,會先請示其Parent使用其搜索路徑幫忙載入,如果Parent 找不到,那么才由自己依照自己的搜索路徑搜索類”,注意喔,這句話具有遞歸性 

下面舉一個例子來說明,為了更好的理解,先弄清楚幾行代碼: 

 1 Public class Test{  2     Public static void main(String[] arg){  3       ClassLoader c  = Test.class.getClassLoader();  //獲取Test類的類加載器 
 4  System.out.println(c);  5       ClassLoader c1 = c.getParent();  //獲取c這個類加載器的父類加載器 
 6  System.out.println(c1);  7       ClassLoader c2 = c1.getParent();//獲取c1這個類加載器的父類加載器 
 8  System.out.println(c2);  9  } 10 }

把以上代碼存到d:\my 文件夾下,直接編譯,然后在dos模式下運行 

D:\my\java Test 

    。。。AppClassLoader。。。 

    。。。ExtClassLoader。。。 

    Null

 

D:\my

 

注: 。。。表示省略了內容 

可以看出Test是由AppClassLoader加載器加載的 

AppClassLoader的Parent 加載器是 ExtClassLoader

 

但是ExtClassLoader的Parent為 null 是怎么回事呵,朋友們留意的話,前面有提到Bootstrap Loader是用C++語言寫的,依java的觀點來看,邏輯上並不存在Bootstrap Loader的類實體,所以在java程序代碼里試圖打印出其內容時,我們就會看到輸出為null 

【注:以下內容大部分引用java深度歷險】 

弄明白了上面的示例,接下來直接進入類裝載的委托模型實例,寫兩個文件,如下: 

文件:Test1.java 

1 Public class Test1{ 2     Public static void main(String[] arg){ 3         System.out.println(Test1.class.getClassLoader()); 4         Test2 t2 = new Test2(); 5  T2.print(); 6  } 7 }

文件: Test2.java 

1 Public class Test2{ 2     Public void prin(){ 3         System.out.println(this.getClass().getClassLoader()); 4  } 5 }

這兩個類的作用就是打印出載入它們的類裝載器是誰, 將這兩個文件保存到d:\my目錄下,編譯后,我們在復制兩份,分別置於 <JRE所在目錄>\classes下(注意,剛開始我們的系統下沒有此目錄,需自己建立) 與 <JRE所在目錄>\lib\ext\classes下(同樣注意,開始我們的系統下也沒此目錄,手工建立), 然后切換到d:\my目錄下開始測試,

 

測試一: 

<JRE所在目錄>\classes下 

Test1.class 

Test2.class

 

<JRE所在目錄>\lib\ext\classes下 

Test1.class 

Test2.class

 

D:\my下 

Test1.class 

Test2.class

 

dos下輸入運行命令,結果如下: 

D:\my>java Test1 

Null 

Null

 

D:\my> 

    

    從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載 入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class. 由於 <JRE所在目錄>\Classes目錄為Bootstrap Loader的搜索路徑之一,所以Bootstrap Loader找到了Test1.class,因此將它載入,接着在Test1.class之內有載入Test2.class的需求,由於 Test1.class是由Bootstrap Loader所載入,所以Test2.class內定是由Bootstrap Loader根據其搜索路徑來找,因Test2.class也位於Bootstrap Loader可以找到的路徑下,所以也被載入了,最后我們看到Test1.class與Test2.class都是由Bootstrap Loader(null)載入。

 

測試二: 

<JRE所在目錄>\classes下 

Test1.class

 

<JRE所在目錄>\lib\ext\classes下 

Test1.class 

Test2.class

 

D:\my下 

Test1.class 

Test2.class

 

dos下輸入運行命令,結果如下: 

D:\my>java Test1 

Null 

Exception in thread “main” java.lang.NoClassdefFoundError:Test2 at Test1.main。。。 

D:\my>

 

    從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載 入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class. 由於 <JRE所在目錄>\Classes目錄為Bootstrap Loader的搜索路徑之一,所以Bootstrap Loader找到了Test1.class,因此將它載入,接着在Test1.class之內有載入Test2.class的需求,由於 Test1.class是由Bootstrap Loader所載入,所以Test2.class內定是由Bootstrap Loader根據其搜索路徑來找,但是因為Bootstrap Loader根本找不到Test2.class(被我們刪除了),而Bootstrap Loader又沒有Parent,所以無法載入Test2.class.最后我們看到Test1.class是由Bootstrap Loader(null)載入,而Test2.class則無法載入

 

測試三 

<JRE所在目錄>\classes下

Test2.class

 

<JRE所在目錄>\lib\ext\classes下 

Test1.class 

Test2.class

 

D:\my下 

Test1.class 

Test2.class

 

dos下輸入運行命令,結果如下: 

D:\my>java Test1 

。。。ExtClassLoader。。。 

Null

 

D:\my>

 

    從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載 入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class.但是Bootstrap Loader無法在其搜索路徑下找到Test1.class(被我們刪掉了),所以ExtClassLoader只得自己搜索,因此 ExtClassLoader在其搜索路徑 <JRE所在目錄>\lib\ext\classes下找到了Test1.class,因此將它載入,接着在Test1.class之內有載 入Test2.class的需求,由於Test1.class是由ExtClassLoader所載入,所以Test2.class內定是由 ExtClassLoader根據其搜索路徑來找,但是因為ExtClassLoader有Parent,所以先由Bootstrap Loader幫忙尋找,Test2.class位於Bootstrap Loader可以找到的路徑下,所以被Bootstrap Loader載入了.最后我們看到Test1.class是由ExtClassLoader載入,而Test2.class則是由Bootstrap Loader(null)載入

 

了解了以上規則,請朋友們自行分析以下場景的執行結果

 

測試四: 

<JRE所在目錄>\classes下

<JRE所在目錄>\lib\ext\classes下 

Test1.class 

Test2.class

 

D:\my下 

Test1.class 

Test2.class

 

測試五: 

<JRE所在目錄>\classes下

<JRE所在目錄>\lib\ext\classes下 

Test1.class

 

D:\my下 

Test1.class 

Test2.class

 

測試六: 

<JRE所在目錄>\classes下

<JRE所在目錄>\lib\ext\classes下

Test2.class

 

D:\my下 

Test1.class 

Test2.class

 

測試七: 

<JRE所在目錄>\classes下

<JRE所在目錄>\lib\ext\classes下

 

D:\my下 

Test1.class 

Test2.class


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM