【JVM】类的生命周期【转+整理】
參考如下三篇并整理。
1.Java類加載機制詳解
2.深入理解Java:類加載機制及反射
3.jvm系列(一):java類的加載機制
?
?
類的生命周期是從被加載到虛擬機內存中開始,到卸載出內存結束。過程共有七個階段。
1.加載---2.驗證---3.準備---3.解析---5.初始化---6.使用---7.卸載
|________連接________|
|______________類的加載過程_____________|
|______________________類的生命周期_______________________|
?
其中類加載的過程包括了加載、驗證、準備、解析、初始化五個階段。
在這五個階段中,加載、驗證、準備和初始化這四個階段發生的順序是確定的,而解析階段則不一定,它在某些情況下可以在初始化階段之后開始,這是為了支持Java語言的運行時綁定(也成為動態綁定或晚期綁定)。
另外注意這里的幾個階段是按順序開始,而不是按順序進行或完成,因為這些階段通常都是互相交叉地混合進行的,通常在一個階段執行的過程中調用或激活另一個階段。
?
【1.加載(裝載)】
在裝載階段,虛擬機需要完成以下3件事情
(1) 通過一個類的全限定名來獲取定義此類的二進制字節流。
(2) 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
(3) 在Java堆中生成一個代表這個類的java.lang.Class對象,作為方法區這些數據的訪問入口。
虛擬機規范中并沒有準確說明二進制字節流應該從哪里獲取以及怎樣獲取,這里可以通過定義自己的類加載器去控制字節流的獲取方式。
?
這里第1條中的二進制字節流并不只是單純地從Class文件中獲取,比如它還可以從Jar包中獲取、從網絡中獲取(最典型的應用便Applet)、由其他文件生成(JSP應用)等。
相對于類加載的其他階段而言,加載階段(準確地說,是加載階段獲取類的二進制字節流的動作)是可控性最強的階段,因為開發人員既以使用系統提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載。
?
?
【2.驗證(校驗、檢查)】
虛擬機如果不檢查輸入的字節流,對其完全信任的話,很可能會因為載入了有害的字節流而導致系統奔潰。
?
1.文件格式驗證:驗證字節流是否符合Class文件格式的規范;例如:是否以0xCAFEBABE開頭、主次版本號是否在當前虛擬機的處理范圍之內、常量池中的常量是否有不被支持的類型。
2.元數據驗證:對字節碼描述的信息進行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的信息符合Java語言規范的要求;例如:這個類是否有父類,除了java.lang.Object之外。
3.字節碼驗證:通過數據流和控制流分析,確定程序語義是合法的、符合邏輯的。
4.符號引用驗證:確保解析動作能正確執行。
驗證階段是非常重要的,但不是必須的,它對程序運行期沒有影響,如果所引用的類經過反復驗證,
那么可以考慮采用-Xverifynone參數來關閉大部分的類驗證措施,以縮短虛擬機類加載的時間。
?
【3.準備】————為類的靜態變量分配內存,并將其初始化為默認值
這個階段正式為類變量(被static修飾的變量)分配內存并設置類變量初始值,這個內存分配是發生在方法區中。
1、注意這里并沒有對實例變量進行內存分配,實例變量將會在對象實例化時隨著對象一起分配在JAVA堆中。
2、這里設置的初始值,通常是指數據類型的‘“零”值。
public?static?int?value?=?123;
value在準備階段過后的初始值為0而不是123,而把value賦值的putstatic指令將在初始化階段才會被執行。
注意:
(1)對基本數據類型來說,對于類變量(static)和全局變量,如果不顯式地對其賦值而直接使用,則系統會為其賦予默認的零值。
(2)對基本數據類型來說,對于局部變量來說,在使用前必須顯式地為其賦值,否則編譯時不通過。
(3)對于同時被static和final修飾的常量,必須在聲明的時候就為其顯式地賦值,否則編譯時不通過;
(4)只被final修飾的常量則既可以在聲明時顯式地賦值,也可以在類初始化時顯式地為其賦值,總之,在使用前必須為其顯式地賦值,系統不會為其賦予默認零值。
(5)對于引用數據類型reference來說,如數組引用、對象引用等,如果沒有對其進行顯式地賦值而直接使用,系統都會為其賦予默認的零值,即null。
(6)如果在數組初始化時沒有對數組中的各元素賦值,那么其中的元素將根據對應的數據類型而被賦予默認的零值。
3、static final常量在準備階段變量value就會被初始化為ConstValue屬性所指定的值,將其結果放入了調用它的類的常量池中。
public static final int value = 3;
編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機就會根據ConstantValue的設置將value賦值為3。
?
下表列出了Java中所有基本數據類型以及reference類型的默認零值:
?
?
?
【4.解析】————把類中的符號引用轉換為直接引用
?
解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程,解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符7類符號引用進行。
符號引用就是一組符號來描述目標,可以是任何字面量。
?
直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄。
?
? 1、類或接口的解析:判斷所要轉化成的直接引用是對數組類型,還是普通的對象類型的引用,從而進行不同的解析。 ? ??2、字段解析:對字段進行解析時,會先在本類中查找是否包含有簡單名稱和字段描述符都與目標相匹配的字段,如果有,則查找結束; 如果沒有,則會按照繼承關系從上往下遞歸搜索該類所實現的各個接口和它們的父接口,還沒有,則按照繼承關系從上往下遞歸搜索其父類,直至查找結束,查找流程如下圖所示: 3、類方法解析:對類方法的解析與對字段解析的搜索步驟差不多,只是多了判斷該方法所處的是類還是接口的步驟,而且對類方法的匹配搜索,是先搜索父類,再搜索接口。 ? ?? 4、接口方法解析:與類方法解析步驟類似,知識接口不會有父類,因此,只遞歸向上搜索父接口就行了。?
【5.初始化】
詳細見此
?類初始化時機:只有當對類的主動使用的時候才會導致類的初始化,類的主動使用包括以下(四大種)六小種:
【1】在如下三個場景(能生成四個字節碼指令的場景),如果類還未初始化,則初始化。
(4個指令【new、getstatic、putstatic、invokestatic】)
A.new
new Test();B.讀取或設置類的靜態變量
int b=Test.a;Test.a=b;C.調用靜態函數
Test.doSomething();【2】反射調用。
Class.forName(“com.mengdd.Test”);【3】子類初始化,要先初始化父類(所有父類)。
【4】虛擬機啟動時,要初始化主類。直接使用java.exe命令來運行某個主類。
?
只有上述四種情況會觸發初始化,也稱為對一個類進行主動引用。
除此以外,有其他方式都不會觸發初始化,稱為被動引用。
注意:
(1)子類引用父類的靜態變量,不會導致子類初始化。
(2)通過數組定義引用類,不會觸發此類的初始化
(3)引用常量時,不會觸發該類的初始化
舉例:
(1)子類引用父類的靜態變量,不會導致子類初始化。
public class SupClass {public static int a = 123;static{System.out.println("supclass init");} }public class SubClass extends SupClass {static{System.out.println("subclass init");} }public class Test {public static void main(String[] args){System.out.println(SubClass.a);} }執行結果:
supclass init 123(2)通過數組定義引用類,不會觸發此類的初始化
public class SupClass {public static int a = 123;static{System.out.println("supclass init");} }public class Test {public static void main(String[] args){SupClass[] spc = new SupClass[10];} }執行結果:
?(3)引用常量時,不會觸發該類的初始化
public class ConstClass {public static final String A= "MIGU";static{System.out.println("ConstCLass init");} }public class TestMain {public static void main(String[] args){System.out.println(ConstClass.A);} }執行結果:
MIGU用final修飾某個類變量時,它的值在編譯時就已經確定好放入常量池了,所以在訪問該類變量時,等于直接從常量池中獲取,并沒有初始化該類。
初始化的步驟
1、如果該類還沒有加載和連接,則程序先加載該類并連接。
2、如果該類的直接父類沒有加載,則先初始化其直接父類。
3、如果類中有初始化語句,則系統依次執行這些初始化語句。
在第二個步驟中,如果直接父類又有直接父類,則系統會再次重復這三個步驟來初始化這個父類,
依次類推,JVM最先初始化的總是java.lang.Object類。
當程序主動使用任何一個類時,系統會保證該類以及所有的父類都會被初始化。
【6.使用】
?
【7.卸載】
?
在如下幾種情況下,Java虛擬機將結束生命周期
1.執行了System.exit()方法
2.程序正常執行結束
3.程序在執行過程中遇到了異常或錯誤而異常終止
4.由于操作系統出現錯誤而導致Java虛擬機進程終止
?
轉載于:https://www.cnblogs.com/CESC4/p/8037858.html
總結
以上是生活随笔為你收集整理的【JVM】类的生命周期【转+整理】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php实现 字符串加密(分类分布分工,
- 下一篇: 斯坦福-随机图模型-week4.0_