JVM | 類加載機(jī)制

類加載過(guò)程

類的生命周期.jpg
  • 加載
    在加載階段,Java虛擬機(jī)需要完成的三件事情:
    ????1、通過(guò)類的全限定名來(lái)獲取類的二進(jìn)制字節(jié)流。
    ????2、將字節(jié)流的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
    ????3、在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問(wèn)入口。


    類加載.jpg
  • 驗(yàn)證
    確保Class文件的字節(jié)流中包含的信息符合《Java虛擬機(jī)的規(guī)范》的全部約束要求,保證這些信息被當(dāng)作代碼運(yùn)行后不會(huì)危害虛擬機(jī)的自身的安全。
    ????1、文件格式驗(yàn)證
    ????2、元數(shù)據(jù)驗(yàn)證
    ????3、字節(jié)碼驗(yàn)證
    ????4、符號(hào)引用驗(yàn)證

  • 準(zhǔn)備
    為類中定義的變量(即靜態(tài)變量,被static修飾的變量)分配內(nèi)存并設(shè)置類變量初始值。

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

解析動(dòng)作主要針對(duì)類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符這7類符號(hào)引用進(jìn)行,分別對(duì)應(yīng)于常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dynamic_info和CONSTANT_InvokeDynamic_info8種常量類型。

有且僅有六種情況對(duì)類進(jìn)行初始化

1、遇到new、getstatic、putstatic或invokestatic這四條字節(jié)碼指令時(shí),如果類型沒(méi)有進(jìn)行初始化,則需要先觸發(fā)其初始化階段。能生成四條指令的場(chǎng)景有:
????1.1、使用new關(guān)鍵字實(shí)例化對(duì)象
????1.2、讀取或設(shè)置一個(gè)類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時(shí)候。
????1.3、調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候。

2、使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候,如果類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。

3、當(dāng)初始化類的時(shí)候,如果發(fā)現(xiàn)其父類沒(méi)有進(jìn)行初始化,則需要先觸發(fā)其父類的初始化。

4、當(dāng)虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含main()方法的那個(gè)類),虛擬機(jī)先初始化這個(gè)主類。

5、當(dāng)java.lang.invoke.MethodHandle實(shí)例解析結(jié)果為REF_getstatic、REF_putstatic、REF_invokestatic、REF_newinvokeSpecial四種類型的方法句柄,并且這個(gè)方法句柄對(duì)應(yīng)的類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。

6、當(dāng)接口中定義了JDK 8新加入的默認(rèn)方法(被default關(guān)鍵字修飾的接口方法)時(shí),如果這個(gè)接口的實(shí)現(xiàn)類發(fā)生了初始化,那接口要在其之前被初始化。

以上6種場(chǎng)景是主動(dòng)引用,當(dāng)被動(dòng)引用時(shí),則不會(huì)觸發(fā)初始化:

1、通過(guò)子類引用父類的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化而不會(huì)觸發(fā)子類的初始化。
2、通過(guò)數(shù)組定義引用類,不會(huì)觸發(fā)此類的初始化,數(shù)組直接繼承java.lang.Obejct,創(chuàng)建動(dòng)作由字節(jié)碼指令newarry觸發(fā)。
3、常量在編譯階段會(huì)存入調(diào)用類的常量池中,本質(zhì)上沒(méi)有直接引用到定義常量的類,因此不會(huì)觸發(fā)定義常量的類的初始化。

類加載器

類加載器.jpg

雙親委派

雙親委派模型的工作過(guò)程是:如果一個(gè)類加載器收到類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到最頂層的啟動(dòng)類加載器中,只有當(dāng)父類加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求(它搜索的范圍中沒(méi)有找到所需的類)時(shí),子加載器才會(huì)嘗試去完成加載。

為什么要搞雙親委派?
主要出于安全考慮,可以保證類只會(huì)被加載一次,避免重復(fù)加載。

ClassLoader源碼解析

ClassLoader的源碼.png

findLoadedClass -> parent.loadClass -> findClass

當(dāng)該類沒(méi)有被自己加載過(guò)時(shí),就調(diào)用父加載器的loadClass方法。如果父類加載器沒(méi)有加載到該類,就使用自己的findClass方法查找該類進(jìn)行加載。如果沒(méi)有找到這個(gè)類則會(huì)拋出ClassNotFoundException異常。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們已經(jīng)講過(guò) JVM 相關(guān)的很多常見(jiàn)知識(shí)點(diǎn),感興趣的朋友可以在我的往期文章中查看。接下來(lái)將繼續(xù)為各位帶來(lái) JVM ...
    Java面典閱讀 293評(píng)論 0 1
  • 代碼編譯的結(jié)果從本地機(jī)器碼轉(zhuǎn)變?yōu)樽止?jié)碼,是存儲(chǔ)格式發(fā)展的一小步,確實(shí)編譯語(yǔ)言發(fā)展的一大步。 虛擬機(jī)把描述類的數(shù)據(jù)從...
    胡二囧閱讀 1,051評(píng)論 0 0
  • 一、類加載機(jī)制概述 ?通過(guò)Class字節(jié)碼那兩篇文章,我們介紹了Java類編譯成Class字節(jié)碼的相關(guān)描述和信息,...
    SunnyMore閱讀 6,062評(píng)論 0 3
  • 一、類加載的時(shí)機(jī) 從類被加載到虛擬機(jī)內(nèi)存中開(kāi)始,到卸載出內(nèi)存為止,它的整個(gè)生命周期分為7個(gè)階段,加載(Loadin...
    Jivanmoon閱讀 616評(píng)論 0 0
  • 前言 本文已經(jīng)收錄到我的Github個(gè)人博客,歡迎大佬們光臨寒舍:我的GIthub博客 學(xué)習(xí)導(dǎo)圖 一.為什么要學(xué)習(xí)...
    許朋友愛(ài)玩閱讀 514評(píng)論 0 3

友情鏈接更多精彩內(nèi)容