溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

好程序員Java學(xué)習(xí)路線分享JVM相關(guān)概念

發(fā)布時(shí)間:2020-08-12 22:18:43 來源:網(wǎng)絡(luò) 閱讀:279 作者:wx5d42865f47214 欄目:編程語言

? ? ?好程序員Java學(xué)習(xí)路線分享JVM相關(guān)概念,jdk(Java Development Kit)Java開發(fā)包,是Java開發(fā)人員用于編譯和調(diào)試程序的一套程序的集合。


? ? jre(Java Runtime Evironment)Java運(yùn)行時(shí)環(huán)境,是運(yùn)行Java程序的平臺(tái),所有的Java程序必須在這個(gè)平臺(tái)中才能執(zhí)行。

? ? jvm(Java Virtual Machine)Java虛擬機(jī),是用代碼虛擬出來的計(jì)算機(jī),模擬執(zhí)行計(jì)算機(jī)的各項(xiàng)功能,它有自己的硬件架構(gòu),如:處理器、堆棧、寄存器等,還有自己的一套指令系統(tǒng),在不同的操作系統(tǒng)上都可以安裝JVM,從而實(shí)現(xiàn)Java程序在不同的操作系統(tǒng)上都能執(zhí)行,JVM就是為實(shí)現(xiàn)Java的跨平臺(tái)特性。

?JVM加載類的過程

我們執(zhí)行Java程序開發(fā)出來后,需要先編譯再執(zhí)行,JVM就負(fù)責(zé)加載類的過程。<br>

類加載的過程分為:

1. 加載

2. 驗(yàn)證

3. 準(zhǔn)備

4. 解析

5. 初始化

類加載的具體過程

下面詳細(xì)介紹下這幾個(gè)過程:

1. 加載<br>

? ? 在加載類的過程要完成:

? ? 1. 根據(jù)類的全名限定符,獲取class二進(jìn)制流,這個(gè)流可以從磁盤上的class、jar文件獲得,也可以從網(wǎng)絡(luò)中獲得。

? ? 2. 將類的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)動(dòng)態(tài)存儲(chǔ)結(jié)構(gòu)

? ? 3. 在內(nèi)存的堆中生成對(duì)應(yīng)的java.lang.Class對(duì)象,作為方法區(qū)的入口

2. 驗(yàn)證<br>

? ? 加載類完成后,就進(jìn)入了驗(yàn)證過程,這個(gè)過程保證了前面生成的Class對(duì)象中的信息,不會(huì)危害JVM的安全。<br>

? ? 需要驗(yàn)證的方面有:

? ? 1. 文件格式驗(yàn)證,是要驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理。如驗(yàn)證魔數(shù)是否0xCAFEBABE;主、次版本號(hào)是否正在當(dāng)前虛擬機(jī)處理范圍之內(nèi);常量池的常量中是否有不被支持的常量類型等等,該驗(yàn)證階段的主要目的是保證輸入的字節(jié)流能正確地解析并存儲(chǔ)于方法區(qū)中,經(jīng)過這個(gè)階段的驗(yàn)證后,字節(jié)流才會(huì)進(jìn)入內(nèi)存的方法區(qū)中存儲(chǔ),所以后面的三個(gè)驗(yàn)證階段都是基于方法區(qū)的存儲(chǔ)結(jié)構(gòu)進(jìn)行的。

? ? 2. 元數(shù)據(jù)驗(yàn)證,是對(duì)字節(jié)碼描述的信息進(jìn)行語義分析,以保證其描述的信息符合Java語言規(guī)范的要求??赡馨ǖ尿?yàn)證如:這個(gè)類是否有父類;這個(gè)類的父類是否繼承了不允許被繼承的類;如果這個(gè)類不是抽象類,是否實(shí)現(xiàn)了其父類或接口中要求實(shí)現(xiàn)的所有方法。

? ? 3. 字節(jié)碼驗(yàn)證,主要工作是進(jìn)行數(shù)據(jù)流和控制流分析,保證被校驗(yàn)類的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的行為。如果一個(gè)類方法體的字節(jié)碼沒有通過字節(jié)碼驗(yàn)證,那肯定是有問題的;但如果一個(gè)方法體通過了字節(jié)碼驗(yàn)證,也不能說明其一定就是安全的。

? ? 4. 符號(hào)引用驗(yàn)證,發(fā)生在虛擬機(jī)將符號(hào)引用轉(zhuǎn)化為直接引用的時(shí)候,這個(gè)轉(zhuǎn)化動(dòng)作將在“解析階段”中發(fā)生。驗(yàn)證符號(hào)引用中通過字符串描述的權(quán)限定名是否能找到對(duì)應(yīng)的類;在指定類中是否存在符合方法字段的描述符及簡單名稱所描述的方法和字段;符號(hào)引用中的類、字段和方法的訪問性(private、protected、public、default)是否可被當(dāng)前類訪問。

3. 準(zhǔn)備<br>

? ? 準(zhǔn)備階段會(huì)在方法區(qū)中為類的靜態(tài)變量分配內(nèi)存,并賦給默認(rèn)值。

? ? ```

? ? public static int count = 100;

? ? ```

? ? 如:上面的count變量在準(zhǔn)備階段會(huì)賦值為0,在初始化時(shí)再賦值為100;

4. 解析<br>

? ? 解析階段是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過程。

? ? - 符號(hào)引用(Symbolic Reference)<br>

? ? ? ? 符號(hào)引用以一組符號(hào)來描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無歧義地定位到目標(biāo)即可。符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān),引用的目標(biāo)并不一定已經(jīng)加載到內(nèi)存中。

? ? - 直接引用(Direct Reference)<br>

? ? ? ? 直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄。直接引用是與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局相關(guān)的,如果有了直接引用,那么引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

5. 初始化<br>

? ? 類初始化是類加載過程的最后一步,前面的類加載過程,除了在加載階段用戶應(yīng)用程序可以通過自定義類加載器參與之外,其余動(dòng)作完全由虛擬機(jī)主導(dǎo)和控制。到了初始化階段,才真正開始執(zhí)行類中定義的Java程序代碼。<br>

? ? 初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過程。<clinit>()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語句塊(static{}塊)中的語句合并產(chǎn)生的。


? ? 那么何時(shí)執(zhí)行初始化呢?

? ? 1. 創(chuàng)建類的實(shí)例

? ? 2. 訪問類的靜態(tài)變量(除常量外,final修飾的)

? ? ? ? 原因:常量一種特殊的變量,因?yàn)榫幾g器把他們當(dāng)作值而不是屬性來對(duì)待。

? ? 3. 訪問類的靜態(tài)方法

? ? 4. 反射如(Class.forName("com.test.Person"))

? ? 5. 當(dāng)初始化一個(gè)類時(shí),發(fā)現(xiàn)其父類還未初始化,則先調(diào)用父類的初始化

? ? 6. 虛擬機(jī)啟動(dòng)時(shí),定義了main()方法的那個(gè)類先初始化

#### 代碼案例

了解了類的加載機(jī)制,我們來看一道面試題:

```

public class MySingleton {


? ? private static MySingleton singleton = new MySingleton();

? ? public static int count1 = 0;

? ? public static int count2;

? ??

? ? private MySingleton(){

? ? ? ? count1++;

? ? ? ? count2++;

? ? }

? ??

? ? public static MySingleton getInstance(){

? ? ? ? return singleton;

? ? }

? ??

? ? public static void main(String[] args) {

? ? ? ? MySingleton singleton = MySingleton.getInstance();

? ? ? ? System.out.println("count1-->"+MySingleton.count1);

? ? ? ? System.out.println("count2-->"+MySingleton.count2);

? ? }

}

```


上面的結(jié)果,大多數(shù)同學(xué)可能認(rèn)為兩個(gè)靜態(tài)變量都是1,結(jié)果比較意外:

```

count1-->0

count2-->1

```

這是為什么呢?下面我們來分析下:

1. 首先我們知道在類的準(zhǔn)備階段會(huì)為靜態(tài)變量賦默認(rèn)值:<br>

singleton = null;

count1 = 0;

count2 = 0;

2. 當(dāng)調(diào)用類的靜態(tài)方法getInstance后,引發(fā)類的初始化,先執(zhí)行new MySingleton() 調(diào)用構(gòu)造方法,這時(shí):<br>

count1 = 1;

count2 = 1;

3. 繼續(xù)初始化,為變量賦值,count1賦值為0,count2沒有賦值就保留值1,結(jié)果就是:<br>

count1 = 0;

count2 = 1;

#### 總結(jié)

JVM是代碼模擬的計(jì)算機(jī),有自己的硬件和軟件,JVM能實(shí)現(xiàn)Java類的加載和運(yùn)行,具體加載過程有:加載、驗(yàn)證、準(zhǔn)備、解析、初始化5個(gè)步驟組成。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI