自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java進(jìn)階之深入理解JVM類加載機(jī)制

開(kāi)發(fā) 后端
從類被加載到虛擬機(jī)內(nèi)存中開(kāi)始,到卸載出內(nèi)存為止,它的整個(gè)生命周期分為7個(gè)階段,加載(Loading)、驗(yàn)證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸載(Unloading)。

前言

了解類加載的過(guò)程,有利于在類初始化時(shí)進(jìn)行一些功能操作;

本文全面講解類加載過(guò)程;

一、類加載介紹

1、類加載生命周期

從類被加載到虛擬機(jī)內(nèi)存中開(kāi)始,到卸載出內(nèi)存為止,它的整個(gè)生命周期分為7個(gè)階段,加載(Loading)、驗(yàn)證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸載(Unloading);

其中驗(yàn)證、準(zhǔn)備、解析三個(gè)部分統(tǒng)稱為連接;

2、類加載的過(guò)程

  • 類加載的過(guò)程包括了加載、驗(yàn)證、準(zhǔn)備、解析、初始化五個(gè)階段;
  • 在這五個(gè)階段中,加載、驗(yàn)證、準(zhǔn)備和初始化這四個(gè)階段發(fā)生的順序是確定的,而解析階段則不一定,它在某些情況下可以在初始化階段之后開(kāi)始,這是為了支持Java語(yǔ)言的運(yùn)行時(shí)綁定(也成為動(dòng)態(tài)綁定或晚期綁定);
  • 另外注意這里的幾個(gè)階段是按順序開(kāi)始,而不是按順序進(jìn)行或完成,因?yàn)檫@些階段通常都是互相交叉地混合進(jìn)行的,通常在一個(gè)階段執(zhí)行的過(guò)程中調(diào)用或激活另一個(gè)階段;

二、類加載過(guò)程詳解

類加載的全過(guò)程:加載、驗(yàn)證、準(zhǔn)備、解析和初始化五個(gè)階段所執(zhí)行的具體操作

1、加載階段

1.1、這在個(gè)階段,虛擬機(jī)要做的事情有如下三個(gè):

通過(guò)一個(gè)類的全限定名來(lái)獲取定義這個(gè)類的二進(jìn)制字節(jié)流;

將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);

在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問(wèn)入口;

這里的二進(jìn)制字節(jié)流并不一定要從Class文件獲取,只要一段二進(jìn)制字節(jié)流符合Class文件的規(guī)范,都可以當(dāng)做一個(gè)Class文件,例如我們可以從jar包中獲取,從網(wǎng)絡(luò)中獲取,運(yùn)行時(shí)生成等,總之獲取Class文件的方式非常多。

1.2、這里需要注意的是通過(guò)一個(gè)類的全限定名來(lái)獲取定義這個(gè)類的二進(jìn)制字節(jié)流的動(dòng)作是有類加載器完成的,對(duì)于非數(shù)組類的加載,我們可以通過(guò)自定義類加載器加載來(lái)進(jìn)行控制。對(duì)于數(shù)組類就不行,因?yàn)樗皇峭ㄟ^(guò)類加載器創(chuàng)建的,而是由Java虛擬機(jī)直接創(chuàng)建的;

但數(shù)組類和非數(shù)組類也有很大的聯(lián)系,畢竟組成數(shù)組的元素就是非數(shù)組類(對(duì)于一維數(shù)組來(lái)說(shuō),而對(duì)于多維數(shù)組來(lái)說(shuō),可以遞歸加載),非數(shù)組類的創(chuàng)建需要類加載器完成。加載創(chuàng)建一個(gè)數(shù)組類的過(guò)程如下:

  • 如果數(shù)組的元素類型是引用類型,就遞歸加載這個(gè)元素類型,這個(gè)數(shù)組將在加載該元素類型的類加載器的類名稱空間上被標(biāo)識(shí);
  • 如果數(shù)組的元素類型不是引用類型(比如int[]數(shù)組),Java虛擬機(jī)將會(huì)把數(shù)組標(biāo)記為與引導(dǎo)類加載器關(guān)聯(lián);
  • 數(shù)組類的可見(jiàn)性與元素類型的可見(jiàn)性一致,如果元素類型是不引用類型,那么數(shù)組的可見(jiàn)性默認(rèn)是public;

2、驗(yàn)證階段

  • 驗(yàn)證的目的是為了確保Class文件中的字節(jié)流包含的信息符合當(dāng)前虛擬機(jī)的要求,而且不會(huì)危害虛擬機(jī)自身的安全。不同的虛擬機(jī)對(duì)類驗(yàn)證的實(shí)現(xiàn)可能會(huì)有所不同,但大致都會(huì)完成以下四個(gè)階段的驗(yàn)證:文件格式的驗(yàn)證、元數(shù)據(jù)的驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證;
  • 文件格式的驗(yàn)證:驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理,該驗(yàn)證的主要目的是保證輸入的字節(jié)流能正確地解析并存儲(chǔ)于方法區(qū)之內(nèi)。經(jīng)過(guò)該階段的驗(yàn)證后,字節(jié)流才會(huì)進(jìn)入內(nèi)存的方法區(qū)中進(jìn)行存儲(chǔ),后面的三個(gè)驗(yàn)證都是基于方法區(qū)的存儲(chǔ)結(jié)構(gòu)進(jìn)行的;
  • 元數(shù)據(jù)驗(yàn)證:對(duì)類的元數(shù)據(jù)信息進(jìn)行語(yǔ)義校驗(yàn)(其實(shí)就是對(duì)類中的各數(shù)據(jù)類型進(jìn)行語(yǔ)法校驗(yàn)),保證不存在不符合Java語(yǔ)法規(guī)范的元數(shù)據(jù)信息;
  • 字節(jié)碼驗(yàn)證:該階段驗(yàn)證的主要工作是進(jìn)行數(shù)據(jù)流和控制流分析,對(duì)類的方法體進(jìn)行校驗(yàn)分析,以保證被校驗(yàn)的類的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的行為;
  • 符號(hào)引用驗(yàn)證:這是最后一個(gè)階段的驗(yàn)證,它發(fā)生在虛擬機(jī)將符號(hào)引用轉(zhuǎn)化為直接引用的時(shí)候(解析階段中發(fā)生該轉(zhuǎn)化,后面會(huì)有講解),主要是對(duì)類自身以外的信息(常量池中的各種符號(hào)引用)進(jìn)行匹配性的校驗(yàn);

3、準(zhǔn)備階段

準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。這個(gè)階段中需要注意兩點(diǎn),首先,這個(gè)時(shí)候進(jìn)行內(nèi)存分配的僅包括類變量(被static修飾的變量)而不包括實(shí)例變量,實(shí)例變量就在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在Java堆中,其次,這里所說(shuō)的初始值通常是數(shù)據(jù)類型的零值;

4、解析階段

  • 解析階段是虛擬機(jī)將常量池中的符號(hào)引用轉(zhuǎn)化為直接引用的過(guò)程。在Class類文件結(jié)構(gòu)一文中已經(jīng)比較過(guò)了符號(hào)引用和直接引用的區(qū)別和關(guān)聯(lián),這里不再贅述。前面說(shuō)解析階段可能開(kāi)始于初始化之前,也可能在初始化之后開(kāi)始,虛擬機(jī)會(huì)根據(jù)需要來(lái)判斷,到底是在類被加載器加載時(shí)就對(duì)常量池中的符號(hào)引用進(jìn)行解析(初始化之前),還是等到一個(gè)符號(hào)引用將要被使用前才去解析它(初始化之后);
  • 對(duì)同一個(gè)符號(hào)引用進(jìn)行多次解析請(qǐng)求時(shí)很常見(jiàn)的事情,虛擬機(jī)實(shí)現(xiàn)可能會(huì)對(duì)第一次解析的結(jié)果進(jìn)行緩存(在運(yùn)行時(shí)常量池中記錄直接引用,并把常量標(biāo)示為已解析狀態(tài)),從而避免解析動(dòng)作重復(fù)進(jìn)行;
  • 解析動(dòng)作主要針對(duì)類或接口、字段、類方法、接口方法四類符號(hào)引用進(jìn)行,分別對(duì)應(yīng)于常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info四種常量類型;

下面是四中符號(hào)符號(hào)引用的解析過(guò)程:

(1) 類或接口的解析

假設(shè)當(dāng)前代碼處于類D中,如果要把一個(gè)從未解析過(guò)的符號(hào)引用N解析為一個(gè)類或接口C的直接引用,虛擬機(jī)將經(jīng)歷如下的過(guò)程:

  • 如果C不是一個(gè)數(shù)組類型,那虛擬機(jī)將會(huì)把代表N的全限定名傳遞給D的類加載器區(qū)加載這個(gè)類。在加載過(guò)程中,由于元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證的需要,又可能觸發(fā)其他相關(guān)類的加載,比如這個(gè)類的父類或?qū)崿F(xiàn)的接口。如果加載過(guò)程出現(xiàn)異常,解析過(guò)程就失敗;
  • 如果C是一個(gè)數(shù)組類型,并且數(shù)組的元素類型是對(duì)象,就會(huì)按照第一點(diǎn)加載數(shù)組元素類型。接著由虛擬機(jī)生成一個(gè)代表著數(shù)組維度和元素的數(shù)組對(duì)象;
  • 如果上面的步驟沒(méi)有異常,那么C在虛擬機(jī)中實(shí)際上已經(jīng)成為一個(gè)有效的類或接口了,但在解析完成之前還要進(jìn)行符號(hào)引用驗(yàn)證,確認(rèn)D是否具備對(duì)C的訪問(wèn)權(quán)限;

(2)字段解析

要解析一個(gè)未被解析過(guò)的字段符號(hào)引用,首先將會(huì)對(duì)字段表內(nèi)class_index項(xiàng)中索引的CONSTANT_Class_info符號(hào)引用進(jìn)行解析,也就是字段所屬的類或借口的符號(hào)引用。如果解析這個(gè)類或接口時(shí)發(fā)生異常,都會(huì)導(dǎo)致解析字段的失敗。如果解析成功,才會(huì)繼續(xù)解析這個(gè)字段。具體的規(guī)則如下:

  • 如果類或接口C本身就包含了簡(jiǎn)單名稱和字段描述符都與目標(biāo)匹配的字段,則返回這個(gè)字段的直接引用,查找結(jié)束;
  • 否則,如果在C中實(shí)現(xiàn)了接口,將會(huì)按照繼承關(guān)系從下到上遞歸搜索各個(gè)接口和它的父接口,如果接口中包含了簡(jiǎn)單名稱和字段描述符都與目標(biāo)匹配的字段,則返回這個(gè)字段的直接引用,查找結(jié)束;
  • 否則,如果C不是Object的話,將會(huì)按照繼承關(guān)系從下到上遞歸搜索父類,如果父類中包含了簡(jiǎn)單名稱和字段描述符都與目標(biāo)匹配的字段,返回這個(gè)字段的直接引用,查找結(jié)束;
  • 否則,查找失敗;
  • 之后,還會(huì)對(duì)返回的字段進(jìn)行權(quán)限驗(yàn)證,如果不具備對(duì)字段的訪問(wèn)權(quán)限,將拋出java.lang.IllegalAccessError異常;

(3)類方法解析

類方法解析的第一個(gè)步驟和字段解析一樣,也需要先解析出類方法表的class_index項(xiàng)中索引的方法所屬的類或接口的符號(hào)引用,如果解析成功,按照如下步驟繼續(xù)(同樣以C來(lái)表示這個(gè)類):

  • 類方法和接口方法符號(hào)引用的常量類型定義是分開(kāi)的,如果在類方法表中發(fā)現(xiàn)class_index中索引的C是一個(gè)接口,直接失敗;
  • 然后,會(huì)在類C中查找這個(gè)方法;
  • 否則,在類C的父類中遞歸查找這個(gè)方法;
  • 否則,在類C實(shí)現(xiàn)的接口列表中遞歸查找這個(gè)方法。如果找到一個(gè)匹配的方法,說(shuō)明類C是一個(gè)抽象類,查找結(jié)束,拋出java.lang.AbstractMethodError異常;
  • 否則,查找失敗,拋出java.lang.NoSuchMethodError異常;
  • 同樣,成功返回后還要進(jìn)行權(quán)限驗(yàn)證;

(4)接口方法解析

接口方法也要解析接口方法表的class_index所屬的類或接口引用,如果解析成功,用C表示這個(gè)類或接口,虛擬機(jī)按照如下的規(guī)則搜索:

  • 與類方法解析不同,如果在接口方法表中發(fā)現(xiàn)class_index是一個(gè)類而不是接口,直接拋出java.lang.IncompatibleClassChangeError異常;
  • 否則,在接口C中查找這個(gè)方法;
  • 否則,在接口C的父接口中遞歸查找;
  • 否則,查找失敗,拋出java.lang.NoSuchMethodError異常;
  • 返回成功后不會(huì)驗(yàn)證權(quán)限,因?yàn)榻涌诘姆椒ǘ际莗ublic的;

5、初始化階段

類初始化階段是類加載過(guò)程的最后一步,前面的類加載過(guò)程中,除了在加載階段用戶應(yīng)用程序可以通過(guò)自定義類加載器參與之外,其余動(dòng)作完全由虛擬機(jī)主導(dǎo)和控制。到了初始化階段,才真正開(kāi)始執(zhí)行類中定義的Java程序。

前面已經(jīng)知道,在準(zhǔn)備階段變量已經(jīng)賦值過(guò)一次系統(tǒng)初始值了,而在初始化階段,則會(huì)根據(jù)程序員通過(guò)程序制定的主觀計(jì)劃去初始化類變量和其它內(nèi)容。即,初始化階段是執(zhí)行類構(gòu)造器<Test>()方法的過(guò)程。下面是<Test>()方法的特點(diǎn):

  • <Test>()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊中的語(yǔ)句合并產(chǎn)生的,編譯器收集的順序由語(yǔ)句塊在源文件中的順序決定的,靜態(tài)語(yǔ)句塊中只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量,定義在之后的變量不能訪問(wèn),但能賦值;
  • <Test>()方法與類構(gòu)造器<Test>()不同,它不需要顯式調(diào)用父類的<Test>()方法,虛擬機(jī)保證在子類<Test>()方法執(zhí)行之前,父類的<Test>()方法已經(jīng)執(zhí)行完畢;
  • 由于父類的<Test>()方法先執(zhí)行,意味著父類中定義的靜態(tài)語(yǔ)句塊要優(yōu)先于子類的變量賦值操作;
  • <Test>()方法對(duì)于類或接口來(lái)說(shuō)并不是必須的,如果一個(gè)類中沒(méi)有靜態(tài)語(yǔ)句塊,也沒(méi)有對(duì)變臉的賦值操作,那么編譯器就不會(huì)生成<Test>()方法;
  • 接口中不能使用靜態(tài)語(yǔ)句塊,但仍然有變量初始化的賦值操作,因此接口與類一樣都會(huì)生成<Test>()方法。但接口與類不同的是,執(zhí)行接口的<Test>()方法不需要先執(zhí)行父接口的<Test>()方法。即,只有使用一個(gè)接口中的變量時(shí),才會(huì)執(zhí)行這個(gè)接口的<Test>()方法;
  • 虛擬機(jī)會(huì)保證一個(gè)類的<Test>()方法在多線程環(huán)境下中被正確的加鎖、同步,如果多個(gè)線程同時(shí)去初始化一個(gè)類,那么只有一個(gè)線程會(huì)去執(zhí)行這個(gè)類的<Test>()方法,其他線程都需要阻塞等待,直到活動(dòng)線程執(zhí)行<Test>()方法完畢;

總結(jié)

類加載這類的文章會(huì)陸續(xù)出幾篇,希望大家喜歡!

本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」

 

責(zé)任編輯:姜華 來(lái)源: Android開(kāi)發(fā)編程
相關(guān)推薦

2021-09-18 06:56:01

JavaCAS機(jī)制

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2016-01-14 09:38:55

Java加載器理解

2021-09-16 06:44:04

Android進(jìn)階流程

2021-09-30 07:36:51

AndroidViewDraw

2021-09-15 07:31:33

Android窗口管理

2021-09-10 07:31:54

AndroidAppStartup原理

2023-11-05 12:05:35

JVM內(nèi)存

2022-10-08 08:34:34

JVM加載機(jī)制代碼

2024-12-30 08:02:40

2017-01-13 22:42:15

iosswift

2024-12-02 09:01:23

Java虛擬機(jī)內(nèi)存

2017-08-08 09:15:41

前端JavaScript頁(yè)面渲染

2021-09-08 06:51:52

AndroidRetrofit原理

2023-10-27 07:47:58

Java語(yǔ)言順序性

2021-02-17 11:25:33

前端JavaScriptthis

2023-10-31 16:00:51

類加載機(jī)制Java

2017-05-03 17:00:16

Android渲染機(jī)制

2023-10-13 13:30:00

MySQL鎖機(jī)制

2011-07-18 14:38:44

子查詢外部查詢
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)