JVM學習記錄-類加載的過程

類的整個生命周期的7個階段是:加載(Loading)湃密、驗證(Verification)畸悬、準備(Preparation)惫谤、解析(Resolution)、初始化(Initialization)际乘、使用(Using)、卸載(Unloading)漂佩。

類加載的全過程主要包括:加載脖含、驗證、準備仅仆、解析器赞、初始化這5個階段的內(nèi)容。

加載

加載是類加載過程的一個階段墓拜, 在加載階段JVM需要完成以下3件事情:

  • 通過一個類的全限定明來獲取定義此類的二進制字節(jié)流港柜。
  • 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)運行時數(shù)據(jù)結(jié)構(gòu)。
  • 在內(nèi)存中生成一個代表這個類的java.lang.Class對象咳榜,作為方法區(qū)這個類的各種數(shù)據(jù)訪問入口夏醉。
    加載階段(準確地說,是加載階段獲取類的二進制字節(jié)流的動作)是整個類加載過程中開發(fā)人員可控性最強的涌韩,因為加載階段既可以使用系統(tǒng)提供的引導類加載器完成畔柔,又可以由用戶自定義的二類加載器去完成,開發(fā)人員可以通過定義自己的類加載器區(qū)控制字節(jié)流的獲取方式臣樱。

加載階段完成后靶擦,虛擬機外部的二進制字節(jié)流就按照虛擬機所需的格式存儲在方法區(qū)中腮考,方法區(qū)中的數(shù)據(jù)存儲格式由虛擬機的實現(xiàn)自行定義,虛擬機規(guī)范未規(guī)定此區(qū)域的具體數(shù)據(jù)結(jié)構(gòu)玄捕。然后再內(nèi)存中實例化一個java.lang.Class類的對象(這個對象踩蔚,并沒有要求必須是在Java堆中,就HotSpot而言枚粘,Class對象比較特殊馅闽,雖然是對象,但是是存放在方法區(qū)中的)馍迄,這個對象將作為程序訪問方法區(qū)中的這些類型數(shù)據(jù)的外部接口福也。

加載階段與連接階段的部分內(nèi)容(如一部分字節(jié)碼文件格式驗證東西)是交叉進行的,但是這兩個階段的開始時間仍然保持著固定的 先后順序攀圈。

驗證

驗證是連接階段的第一步暴凑,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全量承。驗證階段是非常重要的搬设,這個階段是否嚴謹,直接決定了Java虛擬機是否能承受惡意代碼的攻擊撕捍,它大致上會完成4個階段的檢驗工作:文件格式驗證拿穴、元數(shù)據(jù)驗證、字節(jié)碼驗證忧风、符號引用驗證默色。

  • 文件格式驗證

這一階段主要驗證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當前版本的虛擬機處理狮腿。

驗證內(nèi)容包括:是否以魔數(shù)0xCAFEBABE開頭腿宰,主次版本號是否在當前虛擬機處理范圍之內(nèi),常量池的常量是否有不被支持的常量類型缘厢,指向常量的各種索引值是否有指向不存在的常量或不符合類型的常量吃度,CONSTANT_Utf8_info型的常量中是否有不符合UTF8編碼的數(shù)據(jù),Class文件中各個部分及文件本身是否有被刪除的或附近的其他信息等等贴硫。

元數(shù)據(jù)驗證

  • 第二階段主要是對類的元數(shù)據(jù)信息進行語義校驗椿每,保證不存在不符合Java語言規(guī)范的元數(shù)據(jù)信息。

驗證內(nèi)容包括:當前類是否有父類(除了Object類之外英遭,所有類都該有父類)间护,當前類的父類是否繼承了不被允許繼承的類(被final修飾的類),如果當前類不是抽象類挖诸,是否實現(xiàn)了其父類或接口之中要求實現(xiàn)的所有方法汁尺,類中的字段、方法是否與父類產(chǎn)生矛盾(如覆蓋了父類的final字段等)等等多律。

字節(jié)碼驗證

  • 第三階段是整個驗證過程中最復雜的一個階段痴突,主要目的是通過數(shù)據(jù)流和控制流分析搂蜓,確定程序語義是合法的、符合邏輯的苞也。

驗證內(nèi)容包括:保證任意時刻操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作洛勉,例如:保證不會出現(xiàn)在操作棧放置了一個int類型的數(shù)據(jù),使用時卻按long類型來加載如本地變量表中如迟。保證跳轉(zhuǎn)指令不會跳轉(zhuǎn)到方法體以為的字節(jié)碼指令上。保證方法體上的類型轉(zhuǎn)換是有效的攻走,例如:可以把一個子類對象賦值給父類數(shù)據(jù)類型殷勘,但是不能把父類對象賦值給子類數(shù)據(jù)類型。

符號引用驗證

  • 第四個階段的校驗發(fā)生在虛擬機將符號引用轉(zhuǎn)化為直接引用的時候昔搂,這個轉(zhuǎn)化動作發(fā)生在解析階段玲销。符號引用驗證可以看做是對類自身以外的信息進行匹配校驗。

驗證內(nèi)容包括:符號引用通過字符串描述的全限定明是否能找到對應的類摘符。在指定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段贤斜。符號引用中的類、字段逛裤、方法的訪問性是否可以被當前類訪問等等瘩绒。

準備

準備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進行分配带族。這個階段分配內(nèi)存的僅僅是類變量不包括實例變量锁荔。實例變量實在對象實例化的時候分配在堆內(nèi)存中的,還有就是這里給類變量設(shè)置的初始值“通常情況下”下是數(shù)據(jù)類型的零值蝙砌,例如:

public static int value = 888;
這個變量value的值在準備階段被設(shè)置的初始值為0而不是666阳堕,因為此時尚未開始執(zhí)行任何Java方法,而把value賦值為666的putstatic指令是程序編譯后择克,存放于類構(gòu)造器<clinit>()方法之中恬总,所以把value賦值為666的動作將在初始化階段才會執(zhí)行。

上面說到在“通常情況”下初始值是零值肚邢,在非“通常情況”下也就是類字段屬性中存在常量屬性的時候壹堰,那么在準備階段類變量就會被初始化為常量屬性所指定的值。

public static final int value = 888;
編譯時Javac將會生成常量屬性道偷,在準備階段虛擬機就會根據(jù)常量屬性的設(shè)置將value賦值為666缀旁。

解析

解析階段是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程。

  • 符號引用(Symbolic References):符號引用以一組符號來描述所引用的目標勺鸦,符號可以是任何形式的字面量并巍,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關(guān)换途,引用目標并不一定已經(jīng)加載到內(nèi)存中懊渡。

  • 直接引用(Direct References):直接引用可以是直接指向目標的指針刽射、相對偏移量或是一個能間接定位到目標的句柄。直接引用是和虛擬機實現(xiàn)內(nèi)存布局相關(guān)的剃执,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同誓禁。如果有直接引用,那引用的目標必定已經(jīng)在內(nèi)存中存在肾档。

解析動作主要針對類或接口摹恰、字段、類方法怒见、接口方法俗慈、方法類型、方法句柄和調(diào)用點限定符遣耍,這7類符號引用闺阱,分別對應于常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info舵变、CONSTANT_MethodHandle_info和CONSTANT_InvokeDynamic_info 這7中常量類型酣溃。

初始化

類初始化階段是類加載過程的最后一步,前面的類加載過程中纪隙,除了在加載階段用戶應用程序可以通過自定義類加載器參與之外赊豌,其余動作完全由虛擬機主導和控制。在準備階段瘫拣,變量已經(jīng)賦過一次系統(tǒng)初始零值了亿絮,而在初始化階段,是通過程序制定的主觀計劃去初始化類變量和其他資源麸拄,也就是執(zhí)行類構(gòu)造器<clinit>()方法的過程派昧。在上一篇“類的加載時機”中已經(jīng)介紹過了,有5中情況會出發(fā)類初始化拢切,下面介紹的是在<clinit>()方法執(zhí)行過程中一些可能會影響程序運行行為的特點和細節(jié)蒂萎。

  • <clinit>()方法是由編譯器自動收集類中的所有類變量賦值動作和靜態(tài)語句塊(static{})中的語句合并產(chǎn)生的,編譯器收集順序室友語句在源文件中出現(xiàn)的豎線所決定的淮椰,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量五慈,定義在它之后的變量,在前面靜態(tài)語句塊可以賦值主穗,但是不能訪問泻拦。
  • <clinit>()方法與類的構(gòu)造函數(shù)不同,它不需要顯示的調(diào)用父類構(gòu)造器忽媒,所以虛擬機中第一個被執(zhí)行的<clinit>()方法的類肯定是java.lang.Object争拐。
    由于父類的<clinit>()方法先執(zhí)行,也就意味著福利中定義的靜態(tài)語句塊要由于子類的變量賦值操作晦雨。
  • <clinit>()方法對于類或接口來說并不是必需的架曹,如果一個類中沒有靜態(tài)語句塊隘冲,也沒有對變量的賦值操作,那么編譯器可以不為這個類生產(chǎn)<clinit>()方法绑雄。
    接口中不能使用靜態(tài)語句塊展辞,但仍然有變量初始化的賦值操作,因此接口和類一樣都會生成<clinit>()方法万牺。接口中只有在使用父接口的時候才會初始化父接口(上一篇已經(jīng)講解過)罗珍。
    虛擬機會保證一個類的<clinit>()方法在多線程的環(huán)境中被正確地枷鎖、同步脚粟,如果多個線程同時去初始化一個類靡砌,那么只會有一個線程區(qū)執(zhí)行這個類的<clinit>()方法,其他線程都需要阻塞等待珊楼,直到活動線程<clinit>()方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市度液,隨后出現(xiàn)的幾起案子厕宗,更是在濱河造成了極大的恐慌,老刑警劉巖堕担,帶你破解...
    沈念sama閱讀 221,331評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件已慢,死亡現(xiàn)場離奇詭異,居然都是意外死亡霹购,警方通過查閱死者的電腦和手機佑惠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來齐疙,“玉大人膜楷,你說我怎么就攤上這事≌攴埽” “怎么了赌厅?”我有些...
    開封第一講書人閱讀 167,755評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長轿塔。 經(jīng)常有香客問我特愿,道長,這世上最難降的妖魔是什么勾缭? 我笑而不...
    開封第一講書人閱讀 59,528評論 1 296
  • 正文 為了忘掉前任揍障,我火速辦了婚禮,結(jié)果婚禮上俩由,老公的妹妹穿的比我還像新娘毒嫡。我一直安慰自己,他們只是感情好采驻,可當我...
    茶點故事閱讀 68,526評論 6 397
  • 文/花漫 我一把揭開白布审胚。 她就那樣靜靜地躺著匈勋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膳叨。 梳的紋絲不亂的頭發(fā)上洽洁,一...
    開封第一講書人閱讀 52,166評論 1 308
  • 那天,我揣著相機與錄音菲嘴,去河邊找鬼饿自。 笑死,一個胖子當著我的面吹牛龄坪,可吹牛的內(nèi)容都是我干的昭雌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼健田,長吁一口氣:“原來是場噩夢啊……” “哼烛卧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妓局,我...
    開封第一講書人閱讀 39,664評論 0 276
  • 序言:老撾萬榮一對情侶失蹤总放,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后好爬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體局雄,經(jīng)...
    沈念sama閱讀 46,205評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,290評論 3 340
  • 正文 我和宋清朗相戀三年存炮,在試婚紗的時候發(fā)現(xiàn)自己被綠了炬搭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,435評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡穆桂,死狀恐怖宫盔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情充尉,我是刑警寧澤飘言,帶...
    沈念sama閱讀 36,126評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站驼侠,受9級特大地震影響姿鸿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜倒源,卻給世界環(huán)境...
    茶點故事閱讀 41,804評論 3 333
  • 文/蒙蒙 一苛预、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笋熬,春花似錦热某、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筹吐。三九已至,卻和暖如春秘遏,著一層夾襖步出監(jiān)牢的瞬間丘薛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工邦危, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洋侨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,818評論 3 376
  • 正文 我出身青樓倦蚪,卻偏偏與公主長得像希坚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子陵且,可洞房花燭夜當晚...
    茶點故事閱讀 45,442評論 2 359

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

  • 代碼編譯的結(jié)果從本地機器碼轉(zhuǎn)變?yōu)樽止?jié)碼裁僧,是存儲格式發(fā)展的一小步,確實編譯語言發(fā)展的一大步慕购。 虛擬機把描述類的數(shù)據(jù)從...
    胡二囧閱讀 956評論 0 0
  • 1.加載锅知,將二進制字節(jié)流加載到方法區(qū),然后在java堆中實例化一個java.lang.Class類的對象2.驗證:...
    蒸汽飛船閱讀 808評論 0 0
  • 任何程序都需要加載到內(nèi)存才能與CPU進行交流同理, 字節(jié)碼.class文件同樣需要加載到內(nèi)存中,才可以實例化類Cl...
    JavaEdge閱讀 485評論 0 0
  • 1.加載脓钾,將二進制字節(jié)流加載到方法區(qū),然后在java堆中實例化一個java.lang.Class類的對象2.驗證:...
    蒸汽飛船閱讀 262評論 0 0
  • 一、類加載的時機 從類被加載到虛擬機內(nèi)存中開始桩警,到卸載出內(nèi)存為止可训,它的整個生命周期分為7個階段,加載(Loadin...
    Jivanmoon閱讀 567評論 0 0