前面給大家講解了 Java 虛擬的內(nèi)存結(jié)構(gòu) 以及 Java 虛擬機(jī)的垃圾回收機(jī)制拴事,我們更加明白了 Java 的內(nèi)存管理機(jī)制川梅,今天我們來講講 Java 虛擬機(jī)的另外一個(gè)高頻考點(diǎn):類加載機(jī)制。
JVM 的類加載過程分為加載佑吝、驗(yàn)證息罗、準(zhǔn)備芋齿、解析啸如、初始化 5 個(gè)階段。
加載
加載階段由類加載器進(jìn)行負(fù)責(zé)氮惯,類加載器根據(jù)一個(gè)類的全限定名讀取該類的二進(jìn)制字節(jié)流到 JVM 內(nèi)部叮雳,然后轉(zhuǎn)換為一個(gè)對(duì)應(yīng)的 java.lang.Class 對(duì)象實(shí)例想暗;一個(gè)類由類加載器和類本身一起確定,所以不同類加載器加載同一個(gè)類得到的 java.lang.Class 也是不同的帘不。
驗(yàn)證
驗(yàn)證階段負(fù)責(zé)驗(yàn)證類數(shù)據(jù)信息是否符合 JVM 規(guī)范说莫,是否是一個(gè)有效的字節(jié)碼文件。
準(zhǔn)備
準(zhǔn)備階段是正式為類變量(static 修飾的變量)分配內(nèi)存并設(shè)置類變量初始值的階段寞焙,這些內(nèi)存都將在方法區(qū)進(jìn)行分配储狭。
這個(gè)階段由兩個(gè)容易產(chǎn)生混淆的概念需要強(qiáng)調(diào)一下,首先是這時(shí)候進(jìn)行內(nèi)存分配的僅包括類變量捣郊,而不包括實(shí)例變量辽狈,實(shí)例變量將會(huì)在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在 Java 堆中。其次這里所說的初始值在沒有被 final
修飾的時(shí)候都是數(shù)據(jù)類型的零值呛牲,只有類似 public static final int value = 1024
這樣的情況下才回直接被賦值 123刮萌。
解析
解析階段是虛擬機(jī)將常量池內(nèi)的「符號(hào)引用」替換為「直接引用」的過程。
初始化
初始化階段負(fù)責(zé)將所有的 static 域按照程序指定操作對(duì)應(yīng)執(zhí)行(賦值 static 變量娘扩,執(zhí)行 static 塊)着茸。
上述階段通常都是交叉混合允許,沒有嚴(yán)格的先后執(zhí)行順序琐旁。
雙親委派模型
站在 Java 虛擬機(jī)的角度講涮阔,只存在兩種不同的類加載器:一種是啟動(dòng)類加載器,這個(gè)類加載器使用 C++ 語言實(shí)現(xiàn)灰殴,是虛擬機(jī)自身的一部分敬特,另外一種是所有其他的類加載器,這種類加載器都由 Java 語言實(shí)現(xiàn)验懊,獨(dú)立于虛擬機(jī)外部擅羞,并且全部繼承自抽象類 java.lang.ClassLoader
。
雙親委派模型并不是一個(gè)強(qiáng)制性的約束模型义图,而是 Java 設(shè)計(jì)者們推薦給開發(fā)者們的類加載器實(shí)現(xiàn)方式减俏。它的工作過程是:如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類碱工,而是把這個(gè)請(qǐng)求委派給父類加載器去完成娃承,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到頂層的類加載器中怕篷,只有當(dāng)父加載器反饋?zhàn)约和瓿蛇@個(gè)加載請(qǐng)求的時(shí)候历筝,子加載器才會(huì)嘗試自己去加載。
使用雙親委派模型來組織類加載器之間的關(guān)系廊谓,有一個(gè)顯而易見的好處就是 Java 類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系梳猪。
采用雙親委派模型的原因
比如黑客定義一個(gè) java.lang.String 類,該 String 類和系統(tǒng) String 類有一樣的功能蒸痹,只是在某個(gè)方法比如 equels() 中加入了病毒代碼春弥,并且通過自定義類加載器加入 JVM 中呛哟,如果沒有雙親委派模型,那么 JVM 就可能誤以為黑客編寫的 String 類是系統(tǒng) String 類匿沛,導(dǎo)致「病毒代碼」最終被執(zhí)行扫责。而有了雙親委派模型,黑客定義的 java.lang.String 類就用于不會(huì)被加載進(jìn)內(nèi)存逃呼,因?yàn)樽铐敹说念惣虞d器會(huì)加載系統(tǒng)的 String 類鳖孤,最終自定義的類加載器無法加載 java.lang.String 類。
可以通過重寫 loadClass() 方法抡笼,打破雙親委派模型苏揣。
最近的知識(shí)比較枯燥,但還是我們所必須了解的蔫缸。
參考文獻(xiàn):《深入理解 Java 虛擬機(jī)》