設(shè)計(jì)大神云集的網(wǎng)站是網(wǎng)絡(luò)推廣方法怎么樣
【JVM】類的生命周期
文章目錄
- 【JVM】類的生命周期
- 1. 生命周期概述
- 2. 加載階段
- 3. 連接階段
- 3.1 驗(yàn)證
- 3.2 準(zhǔn)備
- 3.3 解析
- 4. 初始化階段
- 4.1 觸發(fā)初始化的方式
- 4.2 clinit不存在的情況
- 4.3 多個(gè)類的初始化
- 5. 總結(jié)
1. 生命周期概述
類的生命周期分為5/7個(gè)階段:
- 加載(Loading)
- 連接(Linking)
- 驗(yàn)證
- 準(zhǔn)備
- 解析
- 初始化(Initialization)
- 使用(Using)
- 卸載(Unloading)
2. 加載階段
- 加載(Loading)階段第一步是類加載器根據(jù)類的全限定名通過不同的渠道以二進(jìn)制流的方式獲取字節(jié)碼信息。
- 類加載器在加載完類之后,Java虛擬機(jī)會將字節(jié)碼中的信息保存到方法區(qū)中。
- 生成一個(gè)InstanceKlass對象,保存類的所有信息,里邊還包括實(shí)現(xiàn)特定功能比如多態(tài)的信息。

- 同時(shí),Java虛擬機(jī)還會在堆中生成一份與方法區(qū)中數(shù)據(jù)類似的java.lang.Class對象。作用是在Java代碼中去獲取類的信息以及存儲靜態(tài)字段的數(shù)據(jù)(jdk8及之后)

對于開發(fā)者來說,只需要訪問堆中的Class對象而不需要訪問方法區(qū)中所有信息。這樣Java虛擬機(jī)就能很好地控制開發(fā)者訪問數(shù)據(jù)的范圍。
3. 連接階段
連接階段可以分為三個(gè)小階段:
- 驗(yàn)證:驗(yàn)證內(nèi)容是否滿足《Java虛擬機(jī)規(guī)范》
- 準(zhǔn)備:給靜態(tài)變量賦初值
- 解析:將常量池中的符號引用替換成指向內(nèi)存的直接引用
3.1 驗(yàn)證
連接(Linking)階段的第一個(gè)環(huán)節(jié)就是驗(yàn)證。驗(yàn)證的主要目的是檢測Java字節(jié)碼文件是否遵守了《Java虛擬機(jī)規(guī)范》中的約束。
主要包括4個(gè)部分(具體詳見 《Java虛擬機(jī)規(guī)范》):
-
文件格式驗(yàn)證:比如檢測字節(jié)碼文件是否以
CAFEBABE
開頭,主次版本號是否滿足當(dāng)前Java虛擬機(jī)版本要求。 -
元信息驗(yàn)證:比如類必須有父類(super不能為空)。
-
驗(yàn)證程序執(zhí)行指令的語義:比如方法內(nèi)的指令執(zhí)行中跳轉(zhuǎn)到不正確的位置。
-
符號引用驗(yàn)證:比如是否訪問了其他類中private的方法等。
3.2 準(zhǔn)備
準(zhǔn)備階段為靜態(tài)變量分配內(nèi)存并設(shè)置初始值,每一種基本數(shù)據(jù)類型和引用數(shù)據(jù)類型都有其初始值。
final
關(guān)鍵字修飾的基本數(shù)據(jù)類型的靜態(tài)變量,在準(zhǔn)備階段直接會將代碼中的值進(jìn)行賦值。
3.3 解析
解析階段主要是將常量池中的符號引用替換為直接引用。符號引用就是在字節(jié)碼文件中使用編號來訪問常量池中的內(nèi)容。


4. 初始化階段
- 初始化階段會執(zhí)行靜態(tài)代碼塊中的代碼,并為靜態(tài)變量賦值。
- 初始化階段會執(zhí)行字節(jié)碼文件中
clinit
部分的字節(jié)碼指令。



我們發(fā)現(xiàn),將靜態(tài)代碼塊和靜態(tài)變量的相對位置發(fā)生變化時(shí),字節(jié)碼指令的位置也會發(fā)生變化。也就是說clinit方法中的執(zhí)行順序與Java中編寫的順序是一致的。
4.1 觸發(fā)初始化的方式
以下有幾種方式會導(dǎo)致類的初始化:
-
訪問一個(gè)類的靜態(tài)變量或者靜態(tài)方法。注意變量是final修飾的并且等號右邊是常量則不會觸發(fā)初始化。
public static final value = 1;//不會觸發(fā)初始化,因?yàn)樵谶B接(準(zhǔn)備)階段就已經(jīng)賦值了。
-
調(diào)用
Class.forName(String className)
。 -
new一個(gè)該類的對象。
-
執(zhí)行main方法的當(dāng)前類。
注:添加 -XX:+TraceClassLoading 參數(shù)可以打印出加載并初始化的類。
4.2 clinit不存在的情況
clinit指令在特定情況下不會出現(xiàn):
- 無靜態(tài)代碼塊且無靜態(tài)變量賦值語句。
- 有靜態(tài)變量的聲明,但是沒有賦值語句。
- 靜態(tài)變量的定義使用
final
關(guān)鍵字,這類變量會在準(zhǔn)備階段直接進(jìn)行初始化。
4.3 多個(gè)類的初始化
- 直接訪問父類的靜態(tài)變量,不會觸發(fā)子類的初始化。
- 子類的初始化
clinit
調(diào)用之前,會先調(diào)用父類的clinit
初始化方法。


5. 總結(jié)
類的生命周期分為5個(gè)階段:
- 加載:根據(jù)類的全限定名把字節(jié)碼文件的內(nèi)容加載并轉(zhuǎn)換成合適的數(shù)據(jù)放入內(nèi)存種種那個(gè),存放在方法區(qū)和堆上。
- 連接:
- 驗(yàn)證:魔數(shù),版本號等驗(yàn)證,一般不需要程序員關(guān)注。
- 準(zhǔn)備:為靜態(tài)變量分配內(nèi)存并設(shè)置初始值。
- 解析:將常量池中的符號引用(編號)替換為直接引用(內(nèi)存地址)
- 初始化:執(zhí)行靜態(tài)代碼塊和靜態(tài)變量的賦值。
- 使用
- 卸載
注意要點(diǎn):
- 靜態(tài)變量使用
final
關(guān)鍵字修飾時(shí),賦值操作在準(zhǔn)備階段就完成了。 - 直接訪問父類的靜態(tài)變量不會觸發(fā)子類的初始化。
- 在子類的初始化
clinit
調(diào)用之前會先調(diào)用父類的clinit
初始化方法。 - 添加
-XX:+TraceClassLoading
參數(shù)可以在控制臺打印出加載并初始化的類