類加載的過程分析

類的生命周期如下圖所示

類的生命周期如下圖所示

類加載的全過程包括加載巷帝,驗證匆笤,準(zhǔn)備研侣,解析,初始化這五個階段炮捧。
本篇文章我們來了解Java虛擬機中這五個階段的具體過程义辕。

加載(Loading)
“加載(Loading)”和“類加載(Class Loading)”是不同的兩個概念,前者是后者的一部分寓盗。在加載階段灌砖,虛擬機需要完成以下三個事情璧函。
①通過一個類的全限定名來獲取其定義的二進制字節(jié)流。
②將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)基显。
③在Java堆中生成一個代表這個類的 java.lang.Class對象蘸吓,作為對方法區(qū)中這些數(shù)據(jù)的訪問入口。

加載階段可控性最強撩幽,在該階段库继,開發(fā)人員可以使用系統(tǒng)提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載窜醉。

加載階段與連接階段的部分內(nèi)容是交叉進行的宪萄,加載階段還沒完成,連接階段就可能已經(jīng)開始了榨惰,但是這兩個階段仍然保持固定的順序開始拜英。

連接(Linking)
驗證
驗證是連接階段的第一步,這一步的目的是為了保證Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機的要求琅催,并且不會影響虛擬機自身的安全性居凶。

驗證階段大致會完成4個階段的檢驗動作:
①文件格式驗證:驗證字節(jié)流是否符合Class文件格式的規(guī)范,例如:是否以 0xCAFEBABE開頭藤抡、主次版本號是否在當(dāng)前虛擬機的處理范圍之內(nèi)侠碧、常量池中的常量是否有不被支持的類型(檢查常量tag標(biāo)志)、指向常量的各種索引值是否有指向不存在的變量或不符合類型的變量等等缠黍。

這個檢驗動作是基于二進制字節(jié)流進行的弄兜,目的是保證輸入的字節(jié)流能夠正確的被解析并存儲到方法區(qū)內(nèi)。

②元數(shù)據(jù)驗證:對字節(jié)碼描述的信息進行語義分析瓷式,以確保描述信息符合Java語言規(guī)范挨队,例如:除了 java.lang.Object之外,這個類是否有父類蒿往;該類的父類是否繼承了不可被繼承的類(final 類);若不是抽象類湿弦,那么是否全部實現(xiàn)父類或者接口中的方法等等瓤漏。

這個檢驗動作是基于方法區(qū)的存儲結(jié)構(gòu)進行的,目的對類的元數(shù)據(jù)信息進行語義檢驗颊埃,以符合Java語言規(guī)范蔬充。

③字節(jié)碼驗證:通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的班利、符合邏輯的饥漫。例如:保證任意時刻操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作;保證方法體的類型轉(zhuǎn)換是有效的等等罗标。

這個檢驗動作是基于方法區(qū)的存儲結(jié)構(gòu)進行的庸队,該階段是最復(fù)雜的一個階段积蜻,對類的方法體進行分析校驗,保證虛擬機的安全性彻消。

④符號引用驗證:確保解析動作能正確執(zhí)行竿拆。發(fā)生在虛擬機將符號引用轉(zhuǎn)為直接引用的時候,這個動作其實是發(fā)生在解析階段宾尚。例如:能否通過類的全限定名找到對應(yīng)的類丙笋。

該驗證非常重要,但是不是必須的煌贴,如果所引用的類經(jīng)過反復(fù)驗證御板,那么可以考慮采用 -Xverifynone參數(shù)來關(guān)閉大部分的類驗證措施,以縮短虛擬機類加載的時間牛郑。

準(zhǔn)備(Preparation)
準(zhǔn)備階段是正式為類變量在方法區(qū)分配內(nèi)存并設(shè)置類變量初始值的階段怠肋。
對于該階段有以下注意點:
①這時候進行內(nèi)存分配的僅包括類變量(static),而不包括實例變量井濒,實例變量會在對象實例化時隨著對象一塊分配在Java堆中灶似。
②這里所設(shè)置的初始值通常情況下是數(shù)據(jù)類型默認的零值(如0、0L瑞你、null酪惭、false等),而不是被在Java代碼中被顯式地賦予的值者甲。

public static int value=3春感;

變量value在準(zhǔn)備階段過后的初始值為0,而不是3虏缸,因為這時候尚未開始執(zhí)行任何Java方法鲫懒,而把value賦值為3的 publicstatic指令是在程序編譯后,存放于類構(gòu)造器 <clinit>()方法之中的刽辙,所以把value賦值為3的動作將在初始化階段才會執(zhí)行窥岩。

③對基本數(shù)據(jù)類型來說,對于類變量(static)和全局變量宰缤,如果不顯式地對其賦值而直接使用颂翼,則系統(tǒng)會為其賦予默認的零值,而對于局部變量來說慨灭,在使用前必須顯式地為其賦值朦乏,否則編譯時不通過。

④對于同時被static和final修飾的常量氧骤,必須在聲明的時候就為其顯式地賦值呻疹,否則編譯時不通過;而只被final修飾的常量則既可以在聲明時顯式地為其賦值筹陵,也可以在類初始化時顯式地為其賦值刽锤,總之镊尺,在使用前必須為其顯式地賦值,系統(tǒng)不會為其賦予默認零值姑蓝。

⑤對于引用數(shù)據(jù)類型reference來說鹅心,如數(shù)組引用、對象引用等纺荧,如果沒有對其進行顯式地賦值而直接使用旭愧,系統(tǒng)都會為其賦予默認的零值,即null宙暇。

⑥如果在數(shù)組初始化時沒有對數(shù)組中的各元素賦值输枯,那么其中的元素將根據(jù)對應(yīng)的數(shù)據(jù)類型而被賦予默認的零值。

⑦ 如果類字段的字段屬性表中存在 ConstantValue屬性占贫,即同時被final和static修飾桃熄,那么在準(zhǔn)備階段變量value就會被初始化為ConstValue屬性所指定的值。
假設(shè)上面的類變量value被定義為:

public static final int value=3型奥;

編譯時Javac將會為value生成ConstantValue屬性瞳收,在準(zhǔn)備階段虛擬機就會根據(jù) ConstantValue的設(shè)置將value賦值為3。我們可以理解為static final常量在編譯期就將其結(jié)果放入了調(diào)用它的類的常量池中

解析(Resolution)
解析階段是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程厢汹,解析動作主要針對類或接口螟深、字段、類方法烫葬、接口方法界弧、方法類型、方法句柄和調(diào)用點限定符七類符號引用進行搭综。

關(guān)于符號引用與直接引用的解釋
符號引用:就是一組符號來描述目標(biāo)垢箕,可以是任何字面量,只要使用時能無歧義的定位到目標(biāo)就行兑巾,與虛擬機的內(nèi)存布局無關(guān)条获。

直接引用:直接指向目標(biāo)的指針萍虽、相對偏移量或一個間接定位到目標(biāo)的句柄觉壶,與虛擬機的內(nèi)存布局有關(guān)致盟。

初始化(Initialization)
類加載過程的最后一步糜颠,前面的類加載過程中,除了加載階段可以由用戶自定義類加載器參與外石挂,其余階段都完全由虛擬機主導(dǎo)和控制。在這個階段,才開始真正的執(zhí)行Java代碼称诗。

準(zhǔn)備階段會進行設(shè)置類變量初始值,在初始化階段头遭,會按照開發(fā)人員自己定義的數(shù)值去初始化類變量和其他資源寓免,也就是說初始化階段是執(zhí)行類構(gòu)造器<clinit>方法的過程癣诱。

區(qū)別于 <init>:其實就是構(gòu)造函數(shù),在生成class文件時袜香,編譯器會在構(gòu)造函數(shù)中添加一些代碼撕予,在本類的構(gòu)造函數(shù)中,會最優(yōu)先調(diào)用父類的<init>函數(shù)蜈首,接著執(zhí)行剩余構(gòu)造函數(shù)中剩余的代碼

什么叫<clinit>()方法與它的注意點
①<clinit>()方法是由編譯器自動收集類中所有的類變量的賦值動作和靜態(tài)語句塊(static{})中的語句合并而產(chǎn)生的实抡。編譯器的收集順序是由語句的出現(xiàn)順序決定,比如靜態(tài)語句塊(static{})只能訪問定義在它之前的變量欢策,定義在它之后的變量只能參與賦值吆寨,不能進行訪問。比如下面的代碼

class Demo{
   static {
       i = 0;//可以進行賦值
      System.out.println(i); //這里無法編譯成功踩寇,提示“非法向前引用”
   }
   static int i = 0;
}

②<clinit>()方法與類的實例構(gòu)造函數(shù)<init>() 方法不同啄清,它不需要顯示的調(diào)用父類的構(gòu)造器,虛擬機會保證在子類的<clinit>()方法執(zhí)行之前俺孙,父類的<clinit>()方法已經(jīng)執(zhí)行完畢辣卒,因此第一個被虛擬機執(zhí)行的<clinit>()方法肯定是java.lang.object。

③由于父類的<clinit>()方法比子類的更先執(zhí)行完畢睛榄,因此父類的靜態(tài)語句塊要優(yōu)先于子類操作荣茫。

④并不是每個類或接口都必須有<clinit>()方法,如果這個類或接口中并沒有靜態(tài)語句塊懈费,也沒有對變量的賦值操作计露。

⑤接口與類一樣都會生成 <clinit>()方法,但是在接口中并不要求父接口全部都完成初始化憎乙,只有在真正使用到父接口的時候它才會被初始化(比如引用接口中定義的常量)票罐,另外接口的實現(xiàn)類在初始化時一樣不會先執(zhí)行接口的 <clinit>()方法。

⑥虛擬機會保證 <clinit>()方法在多線程環(huán)境下會被正確的加鎖泞边,同步该押。因此若一個類的 <clinit>()方法執(zhí)行時需要消耗大量時間,將會引起線程阻塞阵谚。在其他線程阻塞的情況下蚕礼,強制將占用 <clinit>()方法的線程退出,再將其他線程喚醒梢什,這些線程也不會再進入到 <clinit>()方法中奠蹬。同一個類加載器下,一個類型只會被初始化一次嗡午。

結(jié)束生命周期
在如下幾種情況下囤躁,Java虛擬機將結(jié)束生命周期

  • 執(zhí)行了 System.exit()方法
  • 程序正常執(zhí)行結(jié)束
  • 程序在執(zhí)行過程中遇到了異常或錯誤而異常終止
  • 由于操作系統(tǒng)出現(xiàn)錯誤而導(dǎo)致Java虛擬機進程終止

類的加載過程各階段對比

類加載過程分析對比.png

參考文章:https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483934&idx=1&sn=41c46eceb2add54b7cde9eeb01412a90&chksm=ebf6da61dc81537721d36aadb5d20613b0449762842f9128753e716ce5fefe2b659d8654c4e8&scene=21#wechat_redirect

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市狸演,隨后出現(xiàn)的幾起案子言蛇,更是在濱河造成了極大的恐慌,老刑警劉巖宵距,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腊尚,死亡現(xiàn)場離奇詭異,居然都是意外死亡满哪,警方通過查閱死者的電腦和手機婿斥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翩瓜,“玉大人受扳,你說我怎么就攤上這事⊥玫” “怎么了勘高?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坟桅。 經(jīng)常有香客問我华望,道長,這世上最難降的妖魔是什么仅乓? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任赖舟,我火速辦了婚禮,結(jié)果婚禮上夸楣,老公的妹妹穿的比我還像新娘宾抓。我一直安慰自己,他們只是感情好豫喧,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布石洗。 她就那樣靜靜地躺著,像睡著了一般紧显。 火紅的嫁衣襯著肌膚如雪讲衫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天孵班,我揣著相機與錄音涉兽,去河邊找鬼。 笑死篙程,一個胖子當(dāng)著我的面吹牛枷畏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虱饿,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼拥诡,長吁一口氣:“原來是場噩夢啊……” “哼丹允!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袋倔,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎折柠,沒想到半個月后宾娜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡扇售,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年前塔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片承冰。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡华弓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出困乒,到底是詐尸還是另有隱情寂屏,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布娜搂,位于F島的核電站迁霎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏百宇。R本人自食惡果不足惜考廉,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望携御。 院中可真熱鬧昌粤,春花似錦、人聲如沸啄刹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸵膏。三九已至膊升,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谭企,已是汗流浹背廓译。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留债查,地道東北人非区。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像盹廷,于是被迫代替她去往敵國和親征绸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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

  • C/C++在運行前需要完成預(yù)處理、編譯管怠、匯編淆衷、鏈接;而在Java中渤弛,類加載(加載祝拯、連接、初始化)是在程序運行期間第...
    Steven1997閱讀 967評論 1 2
  • 1.加載她肯,將二進制字節(jié)流加載到方法區(qū),然后在java堆中實例化一個java.lang.Class類的對象2.驗證:...
    蒸汽飛船閱讀 808評論 0 0
  • 1.加載佳头,將二進制字節(jié)流加載到方法區(qū),然后在java堆中實例化一個java.lang.Class類的對象2.驗證:...
    蒸汽飛船閱讀 261評論 0 0
  • 搜索的時候看了好幾篇文,自己就想記錄一遍晴氨,加深一下記憶康嘉,以下是原文的地址,受益匪淺籽前。blog.csdn.net/n...
  • 一艘船亭珍, 它在海上顛簸數(shù)日了, 始終還沒有靠岸枝哄。 海風(fēng)吹著波浪块蚌, 一會兒猛, 一會兒弱膘格。 船帆破了峭范, 打了一次次的...
    劉文娟閱讀 651評論 0 2