淺談java類加載機制

淺談java類加載機制

       我們都知道Java源代碼會被編譯器編譯成二進制的 xx.class中間文件,在java程序執行的時候,jvm會把java的class文件加載到內存中,今天就分析一下java類加載的相關知識。類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然後在堆區創建一個 java.lang.Class對象,用來封裝類在方法區內的數據結構,java反射機制的實現就是通過這個Class對象實現的。

 

1.    加載class會有哪些途徑呢?主要有下面幾種方式。

首先是可以從本地系統中直接加載 class文件,也可以從zip,jar等歸檔文件中加載.class文件,還能通過網絡下載.class文件。

 

2.類加載的步驟

    2.1 加載:這一步做的是查找並且加載類的二進制數據

    2.2 連接 ,連接就是將已經讀入到內存的類的二進制數據合併到虛擬機的運行時環境中去。這一步又分爲三個步驟。

      驗證:主要的作用是確保被加載的類的正確性,比如檢查magic number是不是0xcafebabe,檢查版本號,類文件的結構檢查,語義檢查,字節碼驗證,二進制兼容性的驗證等等。

      準備:這個階段是爲類的靜態變量分配內存,並將其初始化爲默認值,注意默認值並不是初始值,例如:

               static int a=2; 

        a是整型,默認值是0,所以準備階段a的值爲0,不是2.

      解析:把類中的符號引用轉換爲直接引用

   2.3初始化:爲類的靜態變量賦予正確的初始值,例如上面的例子中,a會被賦值爲2.

 

 

 

      3.類初始化的時機,即一個java類 什麼時候纔會被初始化。

        一個類被jvm加載時,並不會立即對他初始化,原因是節省內存。一個java類或者接口僅會在首次主動使用時纔會被jvm初始化。

 

    Java程序對類的使用方式可分爲兩種一種是主動使用 ,一種是被動使用。

    主動使用主要有六種情況:

a.      創建這個類的實例,就是new一個新的對象時,這個類會被主動使用。

b.      訪問某個類或接口的靜態變量,或者對該靜態變量賦值

c.       調用類的靜態方法

d.      反射,例如Class.forName(“xx”)

e.      初始化一個類的子類

f.       Java虛擬機啓動時被標明爲啓動類的類

       其他的所有的情況都不是主動使用,類不會被初始化。所以可以知道,類加載的最終結果是位於堆區中的Class對象,Class對象封裝了類在方法區內的數據結構,並且向Java程序員提供了訪問方法區內的數據結構的接口。可以實現反射。

       需要注意的是:調用ClassLoader類的loadClass方法加載一個類,並不是對類的主動使用,不會導致類的初始化。 JVM規範允許類加載器在預料某個類將要被使用時就預先加載它,如果在預先加載的過程中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用該類時才報告錯誤(LinkageError錯誤),如果這個類一直沒有被程序主動使用,那麼類加載器就不會報告錯誤

 

 

4.類加載器的類型

       按照歸屬劃分,有兩種類型的類加載器,分別是jvm自帶的加載器和用戶自定義的加載器。

      4.1 Jvm自帶的加載器又有三個層次。

         根類加載器(Bootstrap),它沒有用java語言實現,它負責加載核心的類庫,例如 java.lang.*。

         擴展類加載器(Extension) ,它是java語言實現的,是java.lang.ClassLoader的子類,負責加載jre\lib\ext下的類。

         系統類加載器(System) 它也是java語言實現的,是java.lang.ClassLoader的子類,負責從classpath下加載類。

       4.2 用戶自定義的類加載器,它是用戶自己實現的,是java.lang.ClassLoader的子類,用戶可以定製類的加載方式,類加載器並不需要等到某個類被“首次主動使用”時再加載它。像tomcat這樣的servlet容器,內部實現就有大量的類加載器。

  類加載器有類似於父子的層級結構,如下圖所示:

   5.類加載的父委託機制。

 

        當使用類加載器加載一個類時,這個類加載器首先判斷這個類是否已經被加載。如果沒有,則請求它的父加載器加載,以此類推,知道根加載器。然後從根加載器開始,若能加載,就加載,否則,再請求子加載器進行加載。如果所有的父加載器和這個加載器本身都不能加載,就拋出一個ClassNotFoundException.這就是類加載的父委託機制。

       加載器的父子關係僅僅是功能職責上的先後關係,並不是java類的繼承關係。

       類加載的父委託機制的作用主要是爲了保證系統的安全,例如,如果用戶自己實現了一個惡意的名爲String的類,但是不能用自己定義的類加載器加載,最終加載的只能是jdk的String類,就保證了安全。

 

6.類的卸載

       一個類被加載,鏈接,初始化後,就開始了它的生命週期。當描述這個類的Class對象不可到達,即沒有引用時,這個類在方法區的數據就會被卸載。

       需要注意的是,被jvm自帶的類加載器加載的類,由於他們一直保持着對這些類的引用,所以永遠不會被卸載。只有被用戶自定義的類加載器加載的類纔有可能被卸載。

7.jvm的生命週期

    當出現這幾種情況時,Java虛擬機的生命週期就結束了。

    7.1執行了System.exit()方法

    7.2程序正常執行結束

    7.3程序在執行過程中遇到了異常或錯誤而異常終止

    7.4由於操作系統出現錯誤而導致Java虛擬機進程終止

 

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