類加載的過(guò)程

一、加載

目的

加載(Loading)階段是整個(gè)類加載過(guò)程中的第一個(gè)階段流椒,需要完成以下三件事情:

  1. 通過(guò)一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流敏簿。可通過(guò)以下方式實(shí)現(xiàn):
    • ZIP 壓縮包中讀取宣虾,最終稱為 JAR惯裕、EAR、WAR 格式的基礎(chǔ)
    • 網(wǎng)絡(luò)中獲取绣硝,最典型應(yīng)用:Web Applet
    • 運(yùn)行時(shí)計(jì)算生成蜻势,多用在動(dòng)態(tài)代理技術(shù)
    • 數(shù)據(jù)庫(kù)中讀取
    • JSP文件生成
    • 加密文件中獲取,典型的防 Class 文件被反編譯的保護(hù)措施
  2. 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
  3. 在方法區(qū)內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象鹉胖,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)訪問(wèn)入口握玛。
非數(shù)組類型的加載

相對(duì)于其他階段,非數(shù)組類型的加載階段(加載階段中獲取類的二進(jìn)制字節(jié)流的動(dòng)作)是開(kāi)發(fā)人員可控性最強(qiáng)的階段甫菠。加載階段既可以使用虛擬機(jī)里內(nèi)置的引導(dǎo)類加載器來(lái)完成挠铲,也可以由用戶自定義的類加載器去完成。

數(shù)組類型的加載

數(shù)組類本身不通過(guò)類加載器創(chuàng)建寂诱,它是由 Java 虛擬機(jī)直接在內(nèi)存中動(dòng)態(tài)構(gòu)造出來(lái)的拂苹。但數(shù)據(jù)類的元素類型(Element Type),即數(shù)組去掉所有維度的類型刹衫,最終還是要靠類加載器來(lái)完成加載醋寝。

一個(gè)數(shù)組類 C 創(chuàng)建過(guò)程遵循以下規(guī)則:

  • 如果數(shù)組的組件類型(Component Type )搞挣,即數(shù)組去掉一個(gè)維度的類型(不同于元素類型)带迟,為引用類型音羞,那就遞歸采用加載(Loading)階段的三個(gè)過(guò)程去加載這個(gè)組件類型,同時(shí)數(shù)組 C 也會(huì)被標(biāo)記在加載此組件類型的類加載器的類名稱空間上仓犬。
  • 如果數(shù)組的組件類型不是引用類型嗅绰,類如 int[] 數(shù)組的組件類型為 int,Java 虛擬機(jī)將會(huì)把數(shù)組 C 標(biāo)記為與引導(dǎo)類加載器關(guān)聯(lián)搀继。
  • 數(shù)組類的可訪問(wèn)性與它的組件類型的可訪問(wèn)性一致窘面,如果組件類型不是引用類型,它的數(shù)組類的可訪問(wèn)性將默認(rèn)為 public叽躯,可被所有的類和接口訪問(wèn)到财边。
存儲(chǔ)位置

加載階段結(jié)束后,Java 虛擬機(jī)外部的二進(jìn)制流就按照虛擬機(jī)所設(shè)定的格式存儲(chǔ)在方法區(qū)中了点骑,方法區(qū)中的數(shù)據(jù)存儲(chǔ)格式完全由各虛擬機(jī)自行定義酣难。

類型數(shù)據(jù)安置在方法區(qū)中后,會(huì)在 Java 堆內(nèi)存中實(shí)例化一個(gè) java.lang.Class 類的對(duì)象黑滴,這個(gè)對(duì)象將作為程序訪問(wèn)方法區(qū)的類型數(shù)據(jù)的外部接口憨募。

二、驗(yàn)證

驗(yàn)證是連接階段的第一步袁辈。

目的
  • 確保 Class 文件的字節(jié)流中包含的信息符合《Java 虛擬機(jī)規(guī)范》的全部要求菜谣。
  • 保證字節(jié)碼編譯運(yùn)行后不會(huì)危害到虛擬機(jī)的自身安全。由于 Class 文件可以由任何途徑產(chǎn)生晚缩,甚至在二進(jìn)制編輯器中完成編寫(xiě)尾膊,故虛擬機(jī)需對(duì)這些字節(jié)碼進(jìn)行嚴(yán)格的校驗(yàn)。

驗(yàn)證階段包含大量驗(yàn)證荞彼,從整體上看冈敛,大致會(huì)完成四個(gè)階段的檢驗(yàn)動(dòng)作:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證卿泽、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證莺债。

文件格式驗(yàn)證

驗(yàn)證字節(jié)流是否符合 Class 文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理签夭。

在 HotSpot 中這一階段可能包括以下驗(yàn)證點(diǎn):

  • 是否以魔數(shù) 0xCAFEBABE 開(kāi)頭
  • 主齐邦、次版本號(hào)是否在當(dāng)前 Java 虛擬機(jī)接受范圍內(nèi)
  • 常量池的常量中是否由不被支持的常量類型
  • CONSTANT_Utf8_info 型的常量中是否有不符合 UTF-8 編碼的數(shù)據(jù)
  • Class 文件中各個(gè)部分及文件本身是否有被刪除的或附加的其他信息
    ......

此階段主要目的是 保證輸入的字節(jié)流能正確地解析并存儲(chǔ)在方法區(qū)。只有此階段驗(yàn)證通過(guò)后第租,字節(jié)流才被允許進(jìn)入 Java 虛擬機(jī)內(nèi)存的方法區(qū)中進(jìn)行存儲(chǔ)措拇,且后面三個(gè)階段全部是基于方法區(qū)存儲(chǔ)結(jié)構(gòu)上進(jìn)行。

元數(shù)據(jù)驗(yàn)證

對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析慎宾,以保證符合《Java 虛擬機(jī)規(guī)范》的全部要求丐吓。

在 HotSpot 中這一階段可能包括以下驗(yàn)證點(diǎn):

  • 這個(gè)類是否有父類(除 java.lang.Object 外浅悉,所有類都應(yīng)當(dāng)有父類)。
  • 這個(gè)類的父類是否繼承了不允許被繼承的類(被 final 修飾的類)券犁。
  • 如果這個(gè)類不是抽象類术健,是否實(shí)現(xiàn)了其父類或接口之中要求實(shí)現(xiàn)的所有方法。
  • 類中的字段粘衬、方法是否與父類產(chǎn)生矛盾荞估,例如:覆蓋了父類的 final 字段、出現(xiàn)不符合規(guī)則的方法重載稚新。
    ......

此階段主要目的是 對(duì)類的元數(shù)據(jù)信息進(jìn)行語(yǔ)義校驗(yàn)勘伺。

字節(jié)碼的驗(yàn)證

四個(gè)過(guò)程中最復(fù)雜的一個(gè)階段,通過(guò)數(shù)據(jù)流分析和控制流分析褂删,確保語(yǔ)義是否合法且符合邏輯飞醉。

在 HotSpot 中這一階段可能包括以下驗(yàn)證點(diǎn):

  • 保證任意時(shí)刻操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作。
    例如:在操作數(shù)棧放置了一個(gè) int 類型的數(shù)據(jù)屯阀,使用時(shí)卻按 long 類型來(lái)加載入本地變量表缅帘。
  • 保證任何跳轉(zhuǎn)指令都不會(huì)跳轉(zhuǎn)到方法體以外的字節(jié)碼指令上。
  • 保證方法體中的類型轉(zhuǎn)換總是有效的蹲盘。
    例如:子類對(duì)象賦值給父類數(shù)據(jù)類型股毫,是安全的,但反過(guò)來(lái)則是危險(xiǎn)的召衔。甚至賦值到其他不相關(guān)數(shù)據(jù)類型铃诬,則是不合法的。

此階段主要目的是 確保語(yǔ)義是否合法且符合邏輯苍凛。

符號(hào)引用驗(yàn)證

發(fā)生在虛擬機(jī)將 符號(hào)引用轉(zhuǎn)化為直接引用的時(shí)候趣席,且在解析階段中發(fā)生孝扛。

符號(hào)引用驗(yàn)證可以看作是對(duì)類自身以外(常量池中的各種符號(hào)引用)的類信息進(jìn)行匹配性校驗(yàn)卷扮,即對(duì)類是否缺少或禁止訪問(wèn)它依賴的某些外部類漱牵、方法是尔、字段等資源進(jìn)行校驗(yàn),無(wú)法通過(guò)驗(yàn)證則拋出 java.lang.IllegalAcessError冷守、java.lang.IllegalAcessError茂嗓、java.lang.IllegalAcessError 等異常尘惧。

在 HotSpot 中這一階段可能包括以下驗(yàn)證點(diǎn):

  • 符號(hào)引用中通過(guò)字符串描述的全限定名是否能找到對(duì)應(yīng)的類惭适。
  • 在指定類中是否存在符合方法的字段描述符及簡(jiǎn)單名稱所描述的方法和字段笙瑟。
  • 符號(hào)引用中的類、字段癞志、方法的可訪問(wèn)性(private往枷、protected、public、<package>)是否可被當(dāng)前類訪問(wèn)错洁。
    ......

此階段主要目的是 確保解析行為能正常進(jìn)行秉宿。

三、準(zhǔn)備

正式為類中定義的變量屯碴,即靜態(tài)變量描睦,進(jìn)行內(nèi)存分配并設(shè)置類變量初始值(零值)的階段。這些變量都存至方法區(qū)內(nèi)存中窿锉。

數(shù)據(jù)類型 int long short char byte boolean float double reference
零值 0 0L (short) 0 '\u0000' (byte) 0 false 0.0f 0.0d null

方法區(qū)的定義:
在 JDK 7 及之前酌摇,HotSpot 使用永久代實(shí)現(xiàn)方法區(qū)膝舅。
在 JDK 8 及之后嗡载,永久代取消,方法存放于元空間(Metaspace)仍稀,類變量會(huì)隨著 Class 對(duì)象一起存放在 Java 堆中洼滚。元空間仍然與堆不相連,但與堆共享物理內(nèi)存技潘,邏輯上可認(rèn)為在堆中遥巴。

四、解析

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

解析發(fā)生的時(shí)間未進(jìn)行規(guī)定铲掐,只要求了在執(zhí)行 anewarraycheckcast值桩、getfield摆霉、getstaticinstanceof奔坟、invokedynamic携栋、invokeinterfaceinvokespecial咳秉、invokestatic婉支、invokevirtualmultianewarray澜建、new向挖、putfieldputstatic 等操作符號(hào)引用的字節(jié)碼指令之前,先對(duì)它們所使用的符號(hào)引用進(jìn)行解析炕舵。

invokedynamic 外何之,虛擬機(jī)可對(duì)第一次解析結(jié)果進(jìn)行緩存,例如:運(yùn)行時(shí)直接引用常量池中的記錄幕侠。并把常量標(biāo)識(shí)為已解析狀態(tài)帝美,避免重復(fù)解析。

類加載過(guò)程包含:
1. 類或接口的解析
2. 字段的解析
3. 方法的解析
4. 接口方法的解析

五、初始化

類加載過(guò)程的最后一個(gè)步驟悼潭。至此庇忌,Java 虛擬機(jī)才真正開(kāi)始執(zhí)行類中編寫(xiě)的 Java 程序代碼,主導(dǎo)權(quán)移交給應(yīng)用程序舰褪。

準(zhǔn)備階段時(shí)皆疹,變量會(huì)賦零值,而在初始化階段占拍,則會(huì)根據(jù)開(kāi)發(fā)人員的主觀想法去進(jìn)行變量初始化略就,即為執(zhí)行 <clinit>() 方法的過(guò)程,<clinit>() 是由 Javac 編譯器自動(dòng)生成的晃酒。

特點(diǎn)
  • <clinit>() 方法是由編譯器自動(dòng)收集類的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static{} 塊)中的語(yǔ)句合并而成的表牢,收集順序由代碼順序決定。靜態(tài)語(yǔ)句塊只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量贝次,定義在它之后的變量崔兴,在前面的靜態(tài)語(yǔ)句塊可以賦值,但是不能訪問(wèn)蛔翅。

  • <clinit>() 方法與類的構(gòu)造函數(shù)(即實(shí)例構(gòu)造器 <init>() 方法)不同敲茄,它不需要顯示地調(diào)用父類構(gòu)造器,Java 虛擬機(jī)會(huì)保證在子類的 <clinit>() 方法執(zhí)行前山析,父類的 <clinit>() 方法已經(jīng)執(zhí)行完畢堰燎。故 JVM 中第一個(gè)被執(zhí)行的 <clinit>() 方法的類型肯定是 java.lang.object

  • 由于父類的 <clinit>() 方法先執(zhí)行笋轨,意味著父類定義的靜態(tài)語(yǔ)句塊要優(yōu)于子類的變量賦值操作秆剪。

  • <clinit>() 方法對(duì)于類和接口來(lái)說(shuō)并不是必需的,若沒(méi)有靜態(tài)語(yǔ)句塊翩腐,也沒(méi)變量賦值操作鸟款,可以不生成 <clinit>() 方法。

  • 接口中不能使用靜態(tài)語(yǔ)句塊茂卦,但仍有變量初始化的賦值操作何什。因此接口與類一樣都會(huì)生成 <clinit>() 方法。但接口與類不同的是等龙,接口不需要先執(zhí)行父接口的 <clinit>() 方法处渣,因?yàn)橹挥挟?dāng)父接口定義的變量被使用時(shí),父接口才會(huì)被初始化蛛砰。接口實(shí)現(xiàn)類初始化時(shí)也一樣不會(huì)執(zhí)行接口的 <clinit>() 方法罐栈。

  • JVM 必須保證一個(gè)類的 <clinit>() 方法在多線程環(huán)境中被正確加鎖同步,如果多個(gè)線程同時(shí)初始化一個(gè)類泥畅,那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類的 <clinit>() 方法荠诬,其他線程需要阻塞等待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市柑贞,隨后出現(xiàn)的幾起案子方椎,更是在濱河造成了極大的恐慌,老刑警劉巖钧嘶,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棠众,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡有决,警方通過(guò)查閱死者的電腦和手機(jī)闸拿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)书幕,“玉大人新荤,你說(shuō)我怎么就攤上這事“粗洌” “怎么了迟隅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)励七。 經(jīng)常有香客問(wèn)我,道長(zhǎng)奔缠,這世上最難降的妖魔是什么掠抬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮校哎,結(jié)果婚禮上两波,老公的妹妹穿的比我還像新娘。我一直安慰自己闷哆,他們只是感情好腰奋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著抱怔,像睡著了一般劣坊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屈留,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天局冰,我揣著相機(jī)與錄音,去河邊找鬼灌危。 笑死康二,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的勇蝙。 我是一名探鬼主播沫勿,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了产雹?” 一聲冷哼從身側(cè)響起烫罩,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洽故,沒(méi)想到半個(gè)月后贝攒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡时甚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年隘弊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒适。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梨熙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刀诬,到底是詐尸還是另有隱情咽扇,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布陕壹,位于F島的核電站质欲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏糠馆。R本人自食惡果不足惜嘶伟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望又碌。 院中可真熱鬧九昧,春花似錦、人聲如沸毕匀。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)皂岔。三九已至蹋笼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凤薛,已是汗流浹背姓建。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缤苫,地道東北人速兔。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像活玲,于是被迫代替她去往敵國(guó)和親涣狗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谍婉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容