一文讀懂Jvm類加載機(jī)制
前言
一個(gè)月沒更新了,這個(gè)月發(fā)生了太多的事情,導(dǎo)致更新的頻率大大降低,不管怎樣收拾心情,技術(shù)的研究不能落下!
jvm作為每個(gè)java程序猿必須了解的知識,博主推薦一本書《深入理解Java虛擬機(jī)》,以前博主在學(xué)校的時(shí)候看過幾遍,每一次看都有新的理解。加上工作了也有一年多的時(shí)間了,有必要好好總結(jié)一番~
什么是jvm
平常我們編寫代碼都是編寫的.java文件,怎么部署到機(jī)器上運(yùn)行呢?通過打jar包或者war包,然后部署運(yùn)行。
如果看過jar包的內(nèi)容那么就能知道,我們寫的.java文件全部被編譯成了.class文件。
這里發(fā)生了很重要的一個(gè)步驟——編譯:將我們寫的程序翻譯成能被jvm讀懂的文件格式。
值得注意的是,每一個(gè)類都會被編譯成一個(gè).class文件,包括內(nèi)部類等。也就是說每一個(gè).class文件都只對應(yīng)我們代碼中的一個(gè)類。
類的生命周期
類被加載到j(luò)vm虛擬機(jī)內(nèi)存開始,到卸載出內(nèi)存為止,他的生命周期可以分為:加載->驗(yàn)證->準(zhǔn)備->解析->初始化->使用->卸載。
下面我們來對此一一說明:
加載
當(dāng)生成一個(gè)jar包以后,我們編寫的程序就全部編編譯成了jvm能讀懂的.class格式。此時(shí)就需要加載了,將我們的編譯好的.class文件加載到j(luò)vm中。此時(shí)就會有一個(gè)“類加載器”的概念。如下圖。
接下來一個(gè)問題,類加載器何時(shí)會將一個(gè).class加載帶jvm?也就是說什么情況下會加載一個(gè)類?
一個(gè)jar包運(yùn)行的時(shí)候會指定一個(gè)main()方法作為入口方法。首先就會將main()方法所在的類加載到j(luò)vm,當(dāng)代碼執(zhí)行遇到new的時(shí)候又繼續(xù)將該對象加載到j(luò)vm。
所以總結(jié)來說,就是在你的代碼中需要用到這個(gè)類的時(shí)候,就會將其加載到j(luò)vm中。
驗(yàn)證
這個(gè)不需要理解的太深,很直白的道理,不能什么阿貓阿狗都能被加載到j(luò)vm中,要不就亂套了。所以該階段就是來校驗(yàn)加載進(jìn)來的.class文件是否符合指定的規(guī)則。
有一個(gè)很有趣的就是,每個(gè).class文件都很浪漫,因?yàn)槊恳粋€(gè).class文件都是以8個(gè)十六進(jìn)制的 0×CAFEBABE,翻譯過來就是咖啡寶貝。浪漫吧?在驗(yàn)證階段的第一步就是檢查.class文件是否以咖啡寶貝來開頭的。
準(zhǔn)備
當(dāng)我們合法的把一個(gè).class文件加載到j(luò)vm中后,此時(shí)就會進(jìn)行一些準(zhǔn)備工作。
首先為這個(gè)類分配內(nèi)存空間,然后為類變量(被static修飾的變量)賦值一個(gè)默認(rèn)的初始值。但是如果類變量同時(shí)被final修飾的話,就不是賦值初始值而是具體的值
用下面兩種情況來說明:
public class Student{ private static int age = 18; } //此時(shí)就會為age變量分配內(nèi)存空間并且為其賦值 0 這個(gè)初始值。
public class Student{ private static final int age = 18; } //age被final修飾,此時(shí)就會為age變量分配內(nèi)存空間并且為其賦值為 18 。
所以我們的流程圖可以更新為
解析
解析階段就是jvm將常量池的符號引用替換為直接引用。
簡單的來說就是我們編寫的代碼中,當(dāng)一個(gè)變量引用某個(gè)對象的時(shí)候,這個(gè)引用在.class文件中是以符號引用來存儲的。在解析階段就需要將其解析為直接引用。如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。
所以我們的流程圖可以更新為
初始化
在準(zhǔn)備階段我們已經(jīng)為加載到j(luò)vm的類分配了內(nèi)存空間并且為類變量賦予了初始值。
而到了初始化階段,才真正開始執(zhí)行類中定義的java程序代碼。主要有以下步驟:
- 為類的靜態(tài)變量賦予正確的初始值。
- 執(zhí)行類的靜態(tài)代碼塊。
按照順序自上而下運(yùn)行類中的變量賦值語句和靜態(tài)語句,并且只有類或接口被Java程序首次主動(dòng)使用時(shí)才初始化他們。如果有父類,則首先按照順序運(yùn)行父類中的變量賦值語句和靜態(tài)語句。
所以我們的流程圖可以更新為
總結(jié)
在一個(gè)靜態(tài)方法中我們是不能直接使用非靜態(tài)變量的。當(dāng)我們使用靜態(tài)方法的時(shí)候,僅僅是初始化了靜態(tài)方法所在的類,此時(shí)只有靜態(tài)變量是被賦了值而非靜態(tài)變量是沒有被賦值的。所以在靜態(tài)方法中是不能直接使用非靜態(tài)變量的。這是我的理解,如果理解有誤,歡迎私信博主或留言哦~
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot整合Druid實(shí)現(xiàn)SQL監(jiān)控和數(shù)據(jù)庫密碼加密
Druid連接池是阿里巴巴開源的數(shù)據(jù)庫連接池項(xiàng)目,Druid連接池為監(jiān)控而生,內(nèi)置強(qiáng)大的監(jiān)控功能,監(jiān)控特性不影響性能,本文給大家介紹了SpringBoot整合Druid實(shí)現(xiàn)SQL監(jiān)控和數(shù)據(jù)庫密碼加密,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2024-06-06SpringBoot?ScheduledTaskRegistrar解決動(dòng)態(tài)定時(shí)任務(wù)思路詳解
本文將從問題出發(fā),詳細(xì)介紹ScheduledTaskRegistrar類是如何解決動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的思路,并給出關(guān)鍵的代碼示例,幫助大家快速地上手學(xué)習(xí)2023-02-02java發(fā)送http get請求的兩種方法(總結(jié))
下面小編就為大家?guī)硪黄猨ava發(fā)送http get請求的兩種方法(總結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05Java源碼解析CopyOnWriteArrayList的講解
今天小編就為大家分享一篇關(guān)于Java源碼解析CopyOnWriteArrayList的講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01關(guān)于Java Spring三級緩存和循環(huán)依賴的深入理解
對于循環(huán)依賴,我相信讀者無論只是聽過也好,還是有過了解也好,至少都有所接觸。但是我發(fā)現(xiàn)目前許多博客對于循環(huán)依賴的講解并不清楚,都提到了Spring的循環(huán)依賴解決方案是三級緩存,但是三級緩存每一級的作用是什么,很多博客都沒有提到,本篇文章帶你深入了解2021-09-09springcloud之自定義簡易消費(fèi)服務(wù)組件
這篇文章主要介紹了springcloud之自定義簡易消費(fèi)服務(wù)組件,本篇來使用rest+ribbon消費(fèi)服務(wù),并且通過輪詢方式來自定義了個(gè)簡易消費(fèi)組件,感興趣的小伙伴們可以參考一下2018-06-06Maven項(xiàng)目web多圖片上傳及格式驗(yàn)證的實(shí)現(xiàn)
本文主要介紹了Maven項(xiàng)目web多圖片上傳及格式驗(yàn)證的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10java正則表達(dá)式判斷強(qiáng)密碼和隨機(jī)生成強(qiáng)密碼代碼示例
這篇文章主要給大家介紹了關(guān)于java正則表達(dá)式判斷強(qiáng)密碼和隨機(jī)生成強(qiáng)密碼的相關(guān)資料,最近需要一個(gè)密碼強(qiáng)度正則表達(dá)式在用戶注冊時(shí)校驗(yàn)用戶密碼強(qiáng)度,需要的朋友可以參考下2023-08-08SpringBoot整合Sa-Token實(shí)現(xiàn)登錄認(rèn)證的示例代碼
本文主要介紹了SpringBoot整合Sa-Token實(shí)現(xiàn)登錄認(rèn)證的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01