官术网_书友最值得收藏!

1.10 JVM的類加載機(jī)制

1.10.1 JVM的類加載階段

JVM的類加載分為5個(gè)階段:加載、驗(yàn)證、準(zhǔn)備、解析、初始化。在類初始化完成后就可以使用該類的信息,在一個(gè)類不再被需要時(shí)可以從JVM中卸載,如圖1-20所示。

圖1-20

1.加載

指JVM讀取Class文件,并且根據(jù)Class文件描述創(chuàng)建java.lang.Class對(duì)象的過程。類加載過程主要包含將Class文件讀取到運(yùn)行時(shí)區(qū)域的方法區(qū)內(nèi),在堆中創(chuàng)建java.lang.Class對(duì)象,并封裝類在方法區(qū)的數(shù)據(jù)結(jié)構(gòu)的過程,在讀取Class文件時(shí)既可以通過文件的形式讀取,也可以通過jar包、war包讀取,還可以通過代理自動(dòng)生成Class或其他方式讀取。

2.驗(yàn)證

主要用于確保Class文件符合當(dāng)前虛擬機(jī)的要求,保障虛擬機(jī)自身的安全,只有通過驗(yàn)證的Class文件才能被JVM加載。

3.準(zhǔn)備

主要工作是在方法區(qū)中為類變量分配內(nèi)存空間并設(shè)置類中變量的初始值。初始值指不同數(shù)據(jù)類型的默認(rèn)值,這里需要注意final類型的變量和非final類型的變量在準(zhǔn)備階段的數(shù)據(jù)初始化過程不同。比如一個(gè)成員變量的定義如下:

public static long value = 1000;

在以上代碼中,靜態(tài)變量value在準(zhǔn)備階段的初始值是0,將value設(shè)置為1000的動(dòng)作是在對(duì)象初始化時(shí)完成的,因?yàn)镴VM在編譯階段會(huì)將靜態(tài)變量的初始化操作定義在構(gòu)造器中。但是,如果將變量value聲明為final類型:

public static final int value = 1000;

則JVM在編譯階段后會(huì)為final類型的變量value生成其對(duì)應(yīng)的ConstantValue屬性,虛擬機(jī)在準(zhǔn)備階段會(huì)根據(jù)ConstantValue屬性將value賦值為1000。

4.解析

JVM會(huì)將常量池中的符號(hào)引用替換為直接引用。

5.初始化

主要通過執(zhí)行類構(gòu)造器的<client>方法為類進(jìn)行初始化。<client>方法是在編譯階段由編譯器自動(dòng)收集類中靜態(tài)語句塊和變量的賦值操作組成的。JVM規(guī)定,只有在父類的<client>方法都執(zhí)行成功后,子類中的<client>方法才可以被執(zhí)行。在一個(gè)類中既沒有靜態(tài)變量賦值操作也沒有靜態(tài)語句塊時(shí),編譯器不會(huì)為該類生成<client>方法。

在發(fā)生以下幾種情況時(shí),JVM不會(huì)執(zhí)行類的初始化流程。

◎ 常量在編譯時(shí)會(huì)將其常量值存入使用該常量的類的常量池中,該過程不需要調(diào)用常量所在的類,因此不會(huì)觸發(fā)該常量類的初始化。

◎ 在子類引用父類的靜態(tài)字段時(shí),不會(huì)觸發(fā)子類的初始化,只會(huì)觸發(fā)父類的初始化。

◎ 定義對(duì)象數(shù)組,不會(huì)觸發(fā)該類的初始化。

◎ 在使用類名獲取Class對(duì)象時(shí)不會(huì)觸發(fā)類的初始化。

◎ 在使用Class.forName加載指定的類時(shí),可以通過initialize參數(shù)設(shè)置是否需要對(duì)類進(jìn)行初始化。

◎ 在使用ClassLoader默認(rèn)的loadClass方法加載類時(shí)不會(huì)觸發(fā)該類的初始化。

1.10.2 類加載器

JVM提供了3種類加載器,分別是啟動(dòng)類加載器、擴(kuò)展類加載器和應(yīng)用程序類加載器,如圖1-21所示。

圖1-21

(1)啟動(dòng)類加載器:負(fù)責(zé)加載Java_HOME/lib目錄中的類庫,或通過-Xbootclasspath參數(shù)指定路徑中被虛擬機(jī)認(rèn)可的類庫。

(2)擴(kuò)展類加載器:負(fù)責(zé)加載Java_HOME/lib/ext目錄中的類庫,或通過java.ext.dirs系統(tǒng)變量加載指定路徑中的類庫。

(3)應(yīng)用程序類加載器:負(fù)責(zé)加載用戶路徑(classpath)上的類庫。

除了上述3種類加載器,我們也可以通過繼承java.lang.ClassLoader實(shí)現(xiàn)自定義的類加載器。

1.10.3 雙親委派機(jī)制

JVM通過雙親委派機(jī)制對(duì)類進(jìn)行加載。雙親委派機(jī)制指一個(gè)類在收到類加載請(qǐng)求后不會(huì)嘗試自己加載這個(gè)類,而是把該類加載請(qǐng)求向上委派給其父類去完成,其父類在接收到該類加載請(qǐng)求后又會(huì)將其委派給自己的父類,以此類推,這樣所有的類加載請(qǐng)求都被向上委派到啟動(dòng)類加載器中。若父類加載器在接收到類加載請(qǐng)求后發(fā)現(xiàn)自己也無法加載該類(通常原因是該類的Class文件在父類的類加載路徑中不存在),則父類會(huì)將該信息反饋給子類并向下委派子類加載器加載該類,直到該類被成功加載,若找不到該類,則JVM會(huì)拋出ClassNotFoud異常。

雙親委派類加載機(jī)制的類加載流程如下,如圖1-22所示。

圖1-22

(1)將自定義加載器掛載到應(yīng)用程序類加載器。

(2)應(yīng)用程序類加載器將類加載請(qǐng)求委托給擴(kuò)展類加載器。

(3)擴(kuò)展類加載器將類加載請(qǐng)求委托給啟動(dòng)類加載器。

(4)啟動(dòng)類加載器在加載路徑下查找并加載Class文件,如果未找到目標(biāo)Class文件,則交由擴(kuò)展類加載器加載。

(5)擴(kuò)展類加載器在加載路徑下查找并加載Class文件,如果未找到目標(biāo)Class文件,則交由應(yīng)用程序類加載器加載。

(6)應(yīng)用程序類加載器在加載路徑下查找并加載Class文件,如果未找到目標(biāo)Class文件,則交由自定義加載器加載。

(7)在自定義加載器下查找并加載用戶指定目錄下的Class文件,如果在自定義加載路徑下未找到目標(biāo)Class文件,則拋出ClassNotFoud異常。

雙親委派機(jī)制的核心是保障類的唯一性和安全性。例如在加載rt.jar包中的java.lang.Object類時(shí),無論是哪個(gè)類加載器加載這個(gè)類,最終都將類加載請(qǐng)求委托給啟動(dòng)類加載器加載,這樣就保證了類加載的唯一性。如果在JVM中存在包名和類名相同的兩個(gè)類,則該類將無法被加載,JVM也無法完成類加載流程。

1.10.4 OSGI

OSGI(Open Service Gateway Initiative)是Java動(dòng)態(tài)化模塊化系統(tǒng)的一系列規(guī)范,旨在為實(shí)現(xiàn)Java程序的模塊化編程提供基礎(chǔ)條件。基于OSGI的程序可以實(shí)現(xiàn)模塊級(jí)的熱插拔功能,在程序升級(jí)更新時(shí),可以只針對(duì)需要更新的程序進(jìn)行停用和重新安裝,極大提高了系統(tǒng)升級(jí)的安全性和便捷性。

OSGI提供了一種面向服務(wù)的架構(gòu),該架構(gòu)為組件提供了動(dòng)態(tài)發(fā)現(xiàn)其他組件的功能,這樣無論是加入組件還是卸載組件,都能被系統(tǒng)的其他組件感知,以便各個(gè)組件之間能更好地協(xié)調(diào)工作。

OSGI不但定義了模塊化開發(fā)的規(guī)范,還定義了實(shí)現(xiàn)這些規(guī)范所依賴的服務(wù)與架構(gòu),市場(chǎng)上也有成熟的框架對(duì)其進(jìn)行實(shí)現(xiàn)和應(yīng)用,但只有部分應(yīng)用適合采用OSGI方式,因?yàn)樗鼮榱藢?shí)現(xiàn)動(dòng)態(tài)模塊,不再遵循JVM類加載雙親委派機(jī)制和其他JVM規(guī)范,在安全性上有所犧牲。

主站蜘蛛池模板: 长宁县| 阜南县| 宜川县| 正蓝旗| 呼和浩特市| 吴川市| 鄱阳县| 商水县| 安仁县| 宜城市| 宿迁市| 正安县| 伊川县| 鄂尔多斯市| 台南市| 白河县| 绿春县| 宁陵县| 浑源县| 浏阳市| 休宁县| 内丘县| 尚志市| 郁南县| 永清县| 乌兰察布市| 淅川县| 潜江市| 南和县| 岢岚县| 绍兴县| 平安县| 西乌珠穆沁旗| 柳江县| 汉川市| 河南省| 荆州市| 吕梁市| 葵青区| 搜索| 房产|