Java類加載過程機制

加載機制:

JVM把class文件加載到內存,並對數據進行校驗、準備、解析、初始化,最終形成JVM可以直接使用的Java類型的過程。

1,加載階段
    加載是類加載過程中的一個階段,不要將這2個概念混淆了。相對於類生命週期的其他階段而言,加載階段(加載階段獲取類的二進制字節流的動作)是可控性最強的階段,因爲開發人員既可以使用系統提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載。

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

1,通過類的全限定名來獲取定義此類的二進制字節流
2,將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構。
3,在內存中生成一個代表這個類的對象,作爲方法區這個類的各種數據的訪問入口。

加載類文件的方式
1,從本地系統中直接加載
2,通過網絡下載
3,從zip,jar等歸檔文件中加載
4,從專有數據庫中提取.class文件
5,將Java源文件動態編譯爲.class文件    

2,鏈接階段(驗證、準備、解析)

3.1 驗證:確保被加載的類的正確性

1,文件格式驗證:驗證字節流是否符合類文件格式的規範,如:0xCAFEBABE開頭、主次版本號等等。
2,元數據驗證:對字節碼進行語義分析如:這個類是否有父類,是否實現了父類的抽象方法等等。
3,字節碼驗證:通過數據流和控制流分析,確定程序語義是合法的、符合邏輯的,如:類型轉換有效等等。
4,符號引用驗證:確保解析動作能正確執行;如:引用能找到對應的類和方法等等。

驗證階段是非常重要的,但不是必須的。可以採用-Xverify:none參數來關閉大部分的類驗證措施。

3.2 準備:爲類的靜態變量分配內存,並將其賦默認值(方法區中)

只對static修飾的靜態變量進行內存分配、賦默認值(如0、0L、null、false等)。
對final的靜態字面值常量直接賦初值(賦初值不是賦默認值,如果不是字面值靜態常量,那麼會和靜態變量一樣賦默認值)。

3.3 解析:將常量池中的符號引用替換爲直接引用(內存地址)的過程

    符號引用就是一組符號來描述目標,如:包括類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符。

    直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄。如指向方法區某個類的一個指針。

4. 初始化:爲類的靜態變量賦初值

1,定義靜態變量時指定初始值。如 public static String a="aa";
2,在靜態代碼塊裏爲靜態變量賦值。如 static{ a="bb"; } 

注意:只有對類的主動使用纔會導致類的初始化。
5. clinit 與 init

在編譯生成class文件時,會產生兩個方法,一個是類的初始化方法clinit, 另一個是實例的初始化方法init。

clinit:是類構造器,主要作用是在類加載過程中的初始化階段進行執行,執行內容包括靜態變量初始化和靜態塊的執行。

init:是實例構造器,主要作用是在類實例化過程中執行,執行內容包括成員變量初始化和代碼塊的執行。

clinit相關:
1. 如果類中沒有靜態變量或靜態代碼塊,那麼clinit方法將不會被生成。
2. 在執行clinit方法時,必須先執行父類的clinit方法。
3. clinit方法只執行一次。
4. static變量的賦值操作和靜態代碼塊的合併順序由源文件中出現的順序決定。

init相關
1. 在執行init方法時,必須先執行父類的init方法。
2. init方法每實例化一次就會執行一次。
3. init方法先爲實例變量分配內存空間,再執行賦默認值,然後根據源碼中的順序執行賦初值或代碼塊

類的引用

1,new一個類的對象。
2,調用類的靜態成員(除了final常量)和靜態方法。
3,使用java.lang.reflect包的方法對類進行反射調用。
4,當虛擬機啓動,先啓動main方法所在的類。
5,當初始化一個類,如果其父類沒有被初始化,則先會初始化他的父類

被動引用

1,當訪問一個靜態域時,只有真正聲明這個域的類纔會被初始化。
例如:通過子類引用父類的靜態變量,不會導致子類初始化。
2,通過數組定義類引用,不會觸發此類的初始化。
3,引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)

類加載器

系統自帶三個類加載器
Bootstrap ClassLoader > Extention ClassLoader > Application ClassLoader > 自定義

1,Bootstrap ClassLoader 
(1)加載核心庫(JAVA_HOME/jre/lib/rt.jar, sun.boot.class.path路徑下的內容)
(2)是用原生代碼(C語言)來實現的,並不繼承自 java.lang.ClassLoader。

2,Extention ClassLoader
(1)加載擴展庫(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路徑下的內容) 
(2)由sun.misc.Launcher$ExtClassLoader實現。

3,Application ClassLoader
(1)它根據應用的類路徑(classpath,java.class.path 路徑下的內容)來加載 Java 類。
(2)由sun.misc.Launcher$AppClassLoader實現。

4,自定義類加載器
通過繼承 java.lang.ClassLoader類的方式實現自己的類加載器,以滿足一些特殊的需求。

雙親委託機制

1,某個特定的類加載器接收到類加載的請求
2,將加載任務委託給自己的父類,直到最高級父類引導類加載器(bootstrap class loader)
3,如果父類能夠加載就加載
4,不能加載則返回到子類進行加載。
5,如果都不能加載則報錯。ClassNotFoundException

雙親委託機制是爲了保證 Java 核心庫的類型安全。這種機制保證不會出現用戶自己能定義java.lang.Object類等的情況。

 

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