JVM類加載機制

在Java語言里雇盖,類的加載、連接和初始化都是在程序運行期間完成的栖忠。這種方式雖然在性能上會一定的開銷崔挖,但是它會是Java的應(yīng)用程序具有更高的靈活性。

比如:

  • 我們在寫一個類中用了一個接口的方法庵寞,現(xiàn)在這個類編譯后的class中是不知道我們具體是哪個類實現(xiàn)的狸相,只有等到了運行程序的時候才指定了具體的實現(xiàn)類。
  • 另外我們可以通過先定義類加載器捐川,然后我們可以隨時從任何地方加載一個二進制流來動態(tài)的加載一個類脓鹃。
  • 可以實現(xiàn)動態(tài)的替換jsp,還有OSGI的熱插拔技術(shù)

這些都是java提供給我們的可以用類加載機制來實現(xiàn)的功能

Java中類的生命周期

加載(Loading)古沥、驗證(Verification)将谊、準(zhǔn)備(Preparation)、解析(Resolution)渐白、初始化(Initialization)尊浓、使用(Using)、卸載(Unloading)

一個類的存在的順序大致會按照這個順序進行纯衍,但是也會存在特殊的情況栋齿,在初始化的時候去解析

Java中什么情況下需要對類進行初始化(階段),再此之前其他的操作:加載襟诸、驗證等已經(jīng)完成

  1. 在使用new創(chuàng)建個對象時瓦堵,或者使用這個類的靜態(tài)方法或者靜態(tài)變量(有一種情況除外,在靜態(tài)變量被final修飾的時候不會初始化對象歌亲,因為這種對象在編譯期間已經(jīng)把變量放在了常量池中了菇用。)
  2. 在使用java.lang.reflect包中的方法,也就是在使用反射的時候會觸發(fā)初始化操作
  3. 初始化一個類的時候如果還沒有初始化父類的時候會初始化父類
  4. 啟動的時候會初始化執(zhí)行類的主類(Main方法)
  5. JDK1.7加上了動態(tài)語言支持陷揪,就是在遇到REF_getStatic惋鸥、REF_putStatic、REF_invokeStatic的方法句柄時這個類如果沒有初始化才會觸發(fā)其初始化悍缠。(就是類似于Js卦绣、Python動態(tài)語言,不需要事先確定這個參數(shù)的類型飞蚓,當(dāng)運行到的時候在去滤港,如果發(fā)現(xiàn)沒有初始化的話再去進行初始化這個類)

加載階段

加載階段虛擬機主要做了這三件事情:

  1. 通過一個類的全限定名(包名+類名)來獲取定義此類的二進制流(得到Class二進制流)
  2. 把這個字節(jié)流鎖代表的經(jīng)他愛存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)(把流轉(zhuǎn)化成方法區(qū)里能夠使用的數(shù)據(jù)結(jié)構(gòu))
  3. 在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口(生成Class對象趴拧,注:Class對象比較特殊溅漾,在HotSpot虛擬機中它是在方法區(qū)內(nèi)不是在堆中)

這三個階段中可控性最強的就是第一個得到二進制流的階段山叮,我們可以通過各種方式來得到,比如從本地磁盤添履,從數(shù)據(jù)庫屁倔,從網(wǎng)絡(luò)上...等到流之后我們可以重寫一個類的加載器的loadClass()方法,或者是使用JDK提供的引導(dǎo)類加載器來完成缝龄。

對于數(shù)組和其他引用類型來說有些區(qū)別汰现,數(shù)組本身是不通過類加載器創(chuàng)建的,它是由Java虛擬機自己直接創(chuàng)建的叔壤。但是數(shù)組類與類加載器還是有很多關(guān)系的瞎饲,具體如下:

  • 如果數(shù)組的組件類型是引用類型(就是數(shù)組去掉第一個維度的類型),那么就是使用對應(yīng)的加載器加載組件類型炼绘,然后數(shù)組將會被組件類型的類加載器上被標(biāo)識嗅战。
  • 如果數(shù)組的組件類型不是引用類型(比如int等基本數(shù)據(jù)類型),java虛擬機將會吧數(shù)組C標(biāo)記與引導(dǎo)類加載器關(guān)聯(lián)
  • 數(shù)組類的可見性與它的組件類型的可見性是一致的俺亮,如果組件類型不是引用類型驮捍,那么這個數(shù)組類的可見性就是public(就是數(shù)組這個類的可見性和它里面的類的可見性是一致的)

驗證階段

驗證階段是為了確保Class文件的字節(jié)流中包含的信息是符合房錢虛擬機要求的。
雖然Java本身是相對安全的脚曾,因為它有編譯成Class這一步倒源。編譯器是有一定規(guī)則的查吊,如果你寫的不符合要求會拒絕編譯。但我們知道Class文件并不是只靠Java源碼編譯過來的,它可以是通過任何途徑獲得(如果你自己用十六進制編輯器寫了一個十六進制文件秘遏,只要符合要求也是可以運行的)胯府。如果虛擬機沒有檢查輸入的字節(jié)流那么可能會導(dǎo)致很嚴(yán)重的后果延届。(可能會有惡意的代碼消玄,或者不是惡意的但會造成程序的崩潰)

大致上驗證階段可以分為以下4個階段:

  • 文件格式驗證
  • 元數(shù)據(jù)驗證
  • 字節(jié)碼驗證
  • 符號引用驗證

準(zhǔn)備階段

準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些都會在方法區(qū)內(nèi)分配撞芍。(這里的分配變量知識分配類變量秧了,就是用static修飾的變量,不包括手里邊了序无,實例變量將會在對象實例化的時候進行賦值验毡。)
下圖是基本數(shù)據(jù)類型的初始值,引用類型的初始值是null


注意:上面說的是通常的情況愉镰,有一種情況是比較特殊的米罚。就是在用final修飾了之后會在初始化階段直接初始化上ConstantValue的值。例如:

public static final int value =123;

這時候在編譯階段會吧value的值附上123了丈探,準(zhǔn)備階段就自然會給value賦值成123

解析階段

在解析階段是把常量池中的符號引用轉(zhuǎn)化成直接引用,符號引用是在編譯成Class文件的時候生成的拔莱。
直接引用和符號引用之間的定義:

  • 符號引用:符號引用用一組符號聊描述所引用的目標(biāo)碗降,符號只需要沒有重復(fù)的能定位到目標(biāo)即可隘竭。這個目標(biāo)是不一定會加載到內(nèi)存中的,也就是說這時候目標(biāo)還不存在讼渊。
  • 直接引用:直接引用是指向目標(biāo)的指針动看、偏移量或者是句柄。直接引用和虛擬機內(nèi)存布局有關(guān)系爪幻。也就是說如果有了直接引用就說明這個目標(biāo)已經(jīng)在內(nèi)存存在了菱皆。

初始化階段

初始化階段必須是在前面的步驟都結(jié)束后才執(zhí)行的最后一步。初始化階段會真正的執(zhí)行類中定義的java程序代碼挨稿。
在初始化階段是執(zhí)行類構(gòu)造器方法的過程(就是執(zhí)行<clinit>類型的方法)仇轻。

  • <clinit>方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊中語句合并產(chǎn)生的結(jié)果(static{})。這個執(zhí)行順序是由語句在源文件出現(xiàn)的順序所決定的奶甘。也就是說靜態(tài)語句塊只能訪問到定義在靜態(tài)語句塊之前的變量篷店,但是定義在后邊的變量可以在前面的靜態(tài)語句塊中被復(fù)制。但是不能訪問
public class Test{
      static{
            i =0臭家;                        
            System.out.print(i);           //這塊會有編譯錯誤非法向前引用
    }
    static int i = 1;
}
  • <clinit>方法與類的構(gòu)造函數(shù)不同疲陕,它不需要顯示的調(diào)用父類的構(gòu)造器,因為虛擬機會保證在子類執(zhí)行之前父類的<clinit>方法一定會執(zhí)行成功钉赁。因此我們可以知道在虛擬機中第一個被執(zhí)行的<clinit>方法一定是java.lang.Object

  • 接口中是不能使用靜態(tài)語句塊的蹄殃,但是可以有靜態(tài)變量賦值的操作。因此也會生成<clinit>方法你踩。但接口月累不同的是執(zhí)行接口不需要先執(zhí)行父接口的方法诅岩,只有當(dāng)付借款使用時才會初始化。

  • 虛擬機會保證一個類的<clinit>方法會被正確的加鎖姓蜂,也就是在多個線程初始化一個類時只會有一個線程去執(zhí)行這個類的方法按厘。注意這種可能會造成阻塞(其他線程不會在重新執(zhí)行一次,一個類只會執(zhí)行一次)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钱慢,一起剝皮案震驚了整個濱河市逮京,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌束莫,老刑警劉巖懒棉,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異览绿,居然都是意外死亡策严,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門饿敲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來妻导,“玉大人,你說我怎么就攤上這事【缶拢” “怎么了术浪?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寿酌。 經(jīng)常有香客問我胰苏,道長,這世上最難降的妖魔是什么醇疼? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任硕并,我火速辦了婚禮,結(jié)果婚禮上秧荆,老公的妹妹穿的比我還像新娘倔毙。我一直安慰自己,他們只是感情好辰如,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布普监。 她就那樣靜靜地躺著,像睡著了一般琉兜。 火紅的嫁衣襯著肌膚如雪凯正。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天豌蟋,我揣著相機與錄音廊散,去河邊找鬼。 笑死梧疲,一個胖子當(dāng)著我的面吹牛允睹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幌氮,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼缭受,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了该互?” 一聲冷哼從身側(cè)響起米者,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宇智,沒想到半個月后蔓搞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡随橘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年喂分,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片机蔗。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒲祈,死狀恐怖甘萧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讳嘱,我是刑警寧澤幔嗦,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布酿愧,位于F島的核電站沥潭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嬉挡。R本人自食惡果不足惜钝鸽,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庞钢。 院中可真熱鬧拔恰,春花似錦、人聲如沸基括。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽风皿。三九已至河爹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桐款,已是汗流浹背咸这。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留魔眨,地道東北人媳维。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像遏暴,于是被迫代替她去往敵國和親侄刽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

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

  • JVM類加載機制 概述 類加載過程 加載 通過類的全限定名獲取類的二進制流 將靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)...
    東溪95閱讀 3,038評論 0 15
  • 簡述:虛擬機把描述類的數(shù)據(jù)從class文件加載到內(nèi)存朋凉,并對數(shù)據(jù)進行校驗州丹、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機直接...
    卡巴拉的樹閱讀 1,881評論 1 6
  • 概述 虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存侥啤,并對數(shù)據(jù)進行校驗当叭,轉(zhuǎn)換,解析和初始化盖灸,最終形成可以被虛擬機直...
    lwd45閱讀 1,703評論 0 16
  • 簡介 代碼編譯的結(jié)果從本地機器碼轉(zhuǎn)變?yōu)樽止?jié)碼蚁鳖,是存儲格式發(fā)展的一小步,卻是編程語言發(fā)展的一大步赁炎。與那些在編譯時需要...
    黃俊彬閱讀 347評論 0 6
  • 一直以來從事的都是成熟的項目公司 所以這種賬號的申請一般不會有機會接觸到 慶幸的是現(xiàn)在入職一家軟件公司 獨立完成i...
    馬鈴薯蜀黍閱讀 952評論 3 1