Java類(lèi)加載過(guò)程梳理,一篇搞定
閱讀本文大概需要 3.5 分鐘。
來(lái)自:blog.csdn.net/hsz2568952354/article/details/96763284
最近在看Java虛擬機(jī),正好看到類(lèi)加載這塊,所以簡(jiǎn)單記錄下所學(xué)到的知識(shí),作為筆記。
首先,我們編寫(xiě)好的Java代碼,經(jīng)過(guò)編譯變成.class文件,然后類(lèi)加載器把.class字節(jié)碼文件加載到JVM中,接著執(zhí)行我們的代碼,最后將類(lèi)卸載出JVM。而從類(lèi)加載到虛擬機(jī)到卸載出虛擬機(jī)的這一整個(gè)生命周期總共可以分為7個(gè)步驟,分別為加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用和卸載,其中驗(yàn)證、準(zhǔn)備和解析又稱(chēng)為連接階段。接下來(lái)簡(jiǎn)單介紹下各個(gè)階段是干嘛的。
加載是“類(lèi)加載”的第一個(gè)階段,就是將需要用到的類(lèi)對(duì)應(yīng)的.class字節(jié)碼文件加載到虛擬機(jī)內(nèi)存,并在方法區(qū)中生成一個(gè)java.lang.Class對(duì)象,作為程序訪問(wèn)這個(gè)類(lèi)的各種數(shù)據(jù)的訪問(wèn)入口。
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????User?user?=?new?User();
????}
}
看一下上面這段代碼,經(jīng)過(guò)編譯會(huì)生成兩個(gè)字節(jié)碼文件Test.class和User.class,接著會(huì)將包含main方法的這個(gè)類(lèi)加載到虛擬機(jī)內(nèi)存中開(kāi)始執(zhí)行,當(dāng)執(zhí)行到User user = new User(),發(fā)現(xiàn)需要用到User類(lèi),就會(huì)將User.class加載到內(nèi)存中。所以簡(jiǎn)單的說(shuō),當(dāng)需要用到哪個(gè)類(lèi)時(shí),就回去加載哪個(gè)類(lèi),Java的自帶的核心類(lèi)會(huì)在虛擬機(jī)啟動(dòng)時(shí)就會(huì)加載,包括包含main方法的啟動(dòng)類(lèi)。但其實(shí),類(lèi)加載也挺復(fù)雜的,只是我了解的也不深,目前就理解成這樣吧,后面再深入研究。
第二階段驗(yàn)證,從字面上就可以看出這個(gè)階段是來(lái)校驗(yàn)加載進(jìn)來(lái)的.class文件中的內(nèi)容是否符合規(guī)范,畢竟編譯成.class文件后還是可以人為的對(duì)這個(gè)文件進(jìn)行修改,那如果改的亂七八糟,壓根不符合虛擬機(jī)的規(guī)范,那虛擬機(jī)就沒(méi)法執(zhí)行了,所以說(shuō)這一步還是比較關(guān)鍵的。至于如何驗(yàn)證,還沒(méi)有研究。
準(zhǔn)備階段我引用《深入理解Java虛擬機(jī)》中的一句話:準(zhǔn)備階段是正式為類(lèi)變量分配內(nèi)存并設(shè)置類(lèi)變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。這也比較好理解,看下面一段代碼:
public?class?Test?{
????public?static?int?value?=?10;
}
當(dāng)需要用到這個(gè)類(lèi)時(shí),會(huì)先將這個(gè)類(lèi)加載到內(nèi)存中,并驗(yàn)證字節(jié)碼文件的合法性。驗(yàn)證通過(guò)后就會(huì)進(jìn)行準(zhǔn)備工作了,會(huì)為這個(gè)類(lèi)中的類(lèi)變量分配內(nèi)存空間,就是上面的value變量,并給一個(gè)初始值。
注意,僅包括類(lèi)變量,不包括實(shí)例變量和局部變量等,并且只是給一個(gè)初始值,int型的初始值是0,所以準(zhǔn)備過(guò)后,value的值是0,而不是10,而真正賦值為10是在初始化階段。我還在其它資料上看到,這一階段也會(huì)給這個(gè)類(lèi)分配內(nèi)存空間,先給類(lèi)分配內(nèi)存,在給它里面的類(lèi)變量分配內(nèi)存。
解析階段是將常量池中的符號(hào)引用替換為直接引用的過(guò)程,這一部分內(nèi)容我還沒(méi)搞懂,所以這里就不過(guò)多記錄了,簡(jiǎn)單了解一下。
初始化階段是類(lèi)加載中核心的一步了,還是以上面的代碼為例,準(zhǔn)備階段我們已經(jīng)為value變量分配了內(nèi)存空間并給了初始值,現(xiàn)在就是真正給value賦值的時(shí)候,把10賦給了value。如果類(lèi)中還含有靜態(tài)代碼塊,也會(huì)在這一階段執(zhí)行。這里還要一點(diǎn)要注意,初始化類(lèi)的時(shí)候,如果父類(lèi)還沒(méi)加載和初始化,也會(huì)觸發(fā)父類(lèi)的加載和初始化。
使用就沒(méi)什么好說(shuō)了,初始化完就可以開(kāi)始使用這個(gè)對(duì)象了。
卸載是類(lèi)的生命周期中的最后一階段,即將方法區(qū)中無(wú)用的類(lèi)回收,而類(lèi)需要同時(shí)滿(mǎn)足下面3個(gè)條件才算無(wú)用的類(lèi):
該類(lèi)所有的實(shí)例都已經(jīng)被回收,也就是Java堆中不存在該類(lèi)的任何實(shí)例。 加載該類(lèi)的ClassLoader已經(jīng)被回收。 該類(lèi)對(duì)應(yīng)的 java.lang.Class對(duì)象沒(méi)有在任何地方被引用,無(wú)法在任何地方通過(guò)反射訪問(wèn)該類(lèi)的方法。
同時(shí)滿(mǎn)足上述3個(gè)條件的類(lèi)即可回收,但不一定就會(huì)回收,可通過(guò)參數(shù)配置。
下面用一張圖來(lái)簡(jiǎn)單展示類(lèi)的加載流程:

以上就是Java類(lèi)的加載過(guò)程,當(dāng)然,只是簡(jiǎn)單的說(shuō)明了一下,剛接觸,還是有很多地方不清楚,先大概有一個(gè)這樣的印象,后面再慢慢深入理解。
參考資料:
《深入理解Java虛擬機(jī)》 《從0開(kāi)始帶你成為JVM實(shí)戰(zhàn)高手》
推薦閱讀:
Ubuntu 22.04正式發(fā)布, 更像蘋(píng)果了
xxl-job驚艷的設(shè)計(jì),怎能叫人不愛(ài)
內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬(wàn)并發(fā)、消息隊(duì)列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper......等技術(shù)棧!
?戳閱讀原文領(lǐng)??!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??朕已閱?

