類加載
在Java代碼中,類型的加載、連接與初始化過程都是在程序運行期間完成的。
類型可以是Class,Interface, 枚舉等。
Java虛擬機與程序的生命周期
在如下幾種情況下,Java虛擬機將結束生命周期
1)執行了System.exit() 方法
2)程序正常執行結束
3)程序在執行過程中遇到了異常或者錯誤而異常終止。
4) 由於操作系統出現錯誤導致Java虛擬機進程終止。
字節碼文件的裝載過程: 加載、連接(包括三個步驟: 驗證 准備 解析)、初始化
加載:查找並加載類的二進制數據
連接:
驗證:確保被加載的類的正確性
准備:為類的靜態變量分配內存,並將其初始化為默認值
解析:把類中的符號引用轉換為直接引用
符號引用:通俗的講,是一種間接引用,如一個類中的方法引用了另外一個類,這是一種符號的表述。
直接引用:就是通過指針的方式,直接指向了目標對象內存的位置,這樣能一下子找到特定的方法。
初始化: 為類的靜態變量賦予正確的初始值
一、類裝載的條件
Java程序對類的使用方式分為兩種
主動使用
被動使用
所有的Java虛擬機實現必須在每個類或接口被Java程序“首次主動使用”時才初始化他們。
Java虛擬機規定: 一個類或者接口在初次使用時,必須進行初始化。
這里的使用指主動使用,主動使用有以下幾種情況:
1) 當創建一個類的實例時。 比如使用new關鍵字,或者通過反射、克隆、反序列化方式。
2) 當調用類的靜態方法時。即當使用了字節碼invokestatic指令
3)當使用類或者接口的靜態字段時(final常量除外,此種情況只會加載類而不會進行初始化),即使用getstatic或者putstatic指令(可以使用jclasslib軟件查看生成的字節碼文件),或者對該靜態字段賦值(putstatic指令)。
4)當使用java.lang.reflect包中的方法反射類的方法時。如Class.forName("com.example.Test")
5)當初始化子類時,必須先初始化父類
6)作為啟動虛擬機,含有main方法的那個類
7)JDK1.7開始提供的動態語言支持
java.lang.invoke.MethodHandle實例的解析結果REF_getStatic, REF_putStatic,REF_invokeStatic句柄對應的類沒有初始化,則初始化。
除了以上情況屬於主動使用外,其他情況均屬於被動使用,被動使用不會引起類的初始化,只是加載了類卻沒有初始化。(這里的初始化就是類加載的第三個階段)
二、類裝載的過程
1、加載類: 處於類裝載的第一個階段
1.1 類的加載指的是將類的.class 文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然后在內存中創建一個java.lang.Class對象(規范中並未說明Class對象位於哪里,HotSpot虛擬機將其放在方法區中),用來分裝類在方法區內的數據結構。
加載類時,JVM必須完成
1)通過類的全名,獲取類的二進制數據流。
2)解析類的二進制數據流為方法區內的數據結構,也就是將類文件放入方法區中
3)創建java.lang.Class類的實例,表示該類型
1.2 加載.class 文件的方式
從本地系統中直接加載
通過網絡下載.class文件
從zip,jar等文檔文件中加載.class 文件
從專有數據庫中提取.class文件
將Java源文件動態編譯為.class 文件(動態代理,JSP(JSP文件本質上是Sevlet,最終被編譯為.class文件))
2、連接
2.1 驗證字節碼
驗證字節碼文件: 當類被加載到系統后,就開始連接操作,驗證就是連接的第一步
主要目的是保證加載的字節碼是否符合規范
驗證的步驟如圖:
2.2 准備階段
當一個類通過驗證后,虛擬機就會進入准備階段。准備階段是正式為類變量(static修飾的變量)分配內存並設置類變量初始值,這些內存都將在方法區進行分配。這個時候進行內存分配的僅是類變量,不包括類實例變量,實例變量將會在對象實例化時隨着對象一起分配在堆上。為類變量設置初始值是設為其數據類型的“零值”。
比如: public static int num = 10; 這個時候就會為num變量賦值為0
Java虛擬機為各種類型變量默認的初始值如表:
注意:java並不支持boolean類型,對於boolean類型,內部實現試Int, 由於int的默認值是0, 故對應的boolean的默認值是false。
如果類中屬於常量的字段,那么常量字段也會在准備階段被附上正確的值,這個賦值屬於java虛擬機的行為,屬於變量的初始化。在准備階段,不會有任何java代碼被執行。
2.3 解析類
解析階段的任務就是將類、接口、字段和方法的符號引用直接轉為直接引用。
符號引用就是一些字面量的引用。比較容易理解的就是在Class類文件中,通過常量池進行大量的符號引用。
3、初始化
為類的靜態變量賦予正確的初始值。
三、類的使用和卸載
除了上面三個步驟,加載,連接,初始化,還有下面兩個過程:使用和卸載
4、使用: 如類創建一個對象,調用類里的方法。
5、卸載:.class 文件加載到內存中,形成了一個自己的數據結構駐留在內存中,還可以從內存中銷毀掉,稱之為卸載。
卸載以后,就不能再創建類的對象了。可以重新加載到內存中。(開發人員很少使用卸載)
以上就是類加載的5個過程。
參考: https://blog.csdn.net/qq_20610631/article/details/82709187