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