Java 進(jìn)階之 - ClassLoader 熱部署及其他

0. 幾個(gè)問(wèn)題

Java 初級(jí)開(kāi)發(fā)工程師經(jīng)常會(huì)遇到的問(wèn)題:

  • 1.Class.forName("com.jdbc.mysql.xxx")是在做什么?
  • 2.讀取配置文件的 this.getClass().getClassLoader().getResourceAsStream("t.conf"),為什么要這么做?
  • 3.聽(tīng)說(shuō)過(guò)類(lèi)加載的雙親委托模型,雙親這個(gè)概念是啥東東?
  • 4.Java 工程為什么修改了代碼,需要重啟才能生效?重啟是做了哪些操作?
  • 5.聽(tīng)說(shuō) tomcat 可以實(shí)現(xiàn)熱部署,如何實(shí)現(xiàn)?

1. 什么是 ClassLoader - 類(lèi)加載?

JVM 的輸入是符合 JVM 規(guī)范的字節(jié)碼(例如經(jīng) javac 編譯 .java 文件生成的 .class(類(lèi))),輸出是實(shí)際執(zhí)行指令;
簡(jiǎn)單來(lái)說(shuō):字節(jié)碼(類(lèi))加載到 JVM 內(nèi)存中,完成一些初始化操作,并轉(zhuǎn)換成 java.lang.Class類(lèi)的一個(gè)實(shí)例。這個(gè)過(guò)程就成為類(lèi)加載。當(dāng)然實(shí)際的類(lèi)加載還做了很多事兒,具體見(jiàn)下文。

到這里問(wèn)題 1 2 就可以解釋下了:

問(wèn)題 1 :Class.forName("com.jdbc.mysql.xxx") 是在做什么?

顯示的方式指定,在運(yùn)行時(shí)加載某個(gè)類(lèi),這是類(lèi)加載動(dòng)態(tài)性的一種表現(xiàn)。

問(wèn)題 2:讀取配置文件的 this.getClass().getClassLoader().getResourceAsStream("t.conf"),為什么要這么做?

實(shí)際的 ClassLoader 也支持加載資源文件到 JVM(實(shí)際上不管是 .class 還是 .properties 都是文件),直接把資源文件讀成流加載,這種方式只需要把資源文件放到 classpath 中即可,因?yàn)椴还苁羌虞d什么,都是從 classpath 中加載。

2. ClassLoader 做了哪些事?

主要有 3 個(gè)階段:loading -> linking -> initalization,如下圖所示:


image.png

2.1 loading 階段

通過(guò)一定策略(雙親委派?)加載字節(jié)碼生成 class 對(duì)象。字節(jié)碼來(lái)源可以是本地的字節(jié)碼文件也可以是網(wǎng)絡(luò)字節(jié)流。

加載存儲(chǔ)二進(jìn)制數(shù)據(jù)(例如: fully qualified class-name, immediate parent class-name, information about methods, variables, constructors etc.)到 JVM 方法區(qū)。對(duì)于每個(gè).class 文件,JVM 創(chuàng)建一個(gè)類(lèi)對(duì)象(java.lang.class)到 JVM 堆,不管多少次調(diào)用,類(lèi)對(duì)象只生成一次。

主要有三種類(lèi)型的 ClassLoader 。bootstrap -> extension -> application。具體 classloader 過(guò)程下面會(huì)講述。

2.2 lingking 階段

  • 驗(yàn)證:目的在于確保Class文件的字節(jié)流中包含信息符合當(dāng)前虛擬機(jī)要求,不會(huì)危害虛擬機(jī)自身安全。主要包括四種驗(yàn)證,文件格式驗(yàn)證,元數(shù)據(jù)驗(yàn)證,字節(jié)碼驗(yàn)證,符號(hào)引用驗(yàn)證。

  • 準(zhǔn)備:為類(lèi)變量 (即 static 修飾的字段變量) 分配內(nèi)存并且設(shè)置該類(lèi)變量的初始值即 0 (如 static int i=5;這里只將 i 初始化為 0,至于 5 的值將在初始化時(shí)賦值),這里不包含用 final 修飾的 static,因?yàn)閒inal在編譯的時(shí)候就會(huì)分配了,注意這里不會(huì)為實(shí)例變量分配初始化,類(lèi)變量會(huì)分配在方法區(qū)中,而實(shí)例變量是會(huì)隨著對(duì)象一起分配到 Java 堆中。

  • 解析:主要將常量池中的符號(hào)引用替換為直接引用的過(guò)程。符號(hào)引用就是一組符號(hào)來(lái)描述目標(biāo),可以是任何字面量,而直接引用就是直接指向目標(biāo)的指針、相對(duì)偏移量或一個(gè)間接定位到目標(biāo)的句柄。有類(lèi)或接口的解析,字段解析,類(lèi)方法解析,接口方法解析。

2.3 initalization 階段

類(lèi)加載最后階段,若該類(lèi)具有超類(lèi),則對(duì)其進(jìn)行初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化成員變量 (如前面只初始化了默認(rèn)值的 static 變量將會(huì)在這個(gè)階段賦值,成員變量也將被初始化)。

3.classloader 怎么加載?什么是雙親委派?

回到問(wèn)題 3:

3.聽(tīng)說(shuō)過(guò)類(lèi)加載的雙親委托模型,雙親這個(gè)概念是啥東東?

實(shí)際上問(wèn)題 3 中說(shuō)的雙親委派 翻譯容易引起歧義。Parents delegation model,個(gè)人認(rèn)為翻譯成:“父類(lèi)委派模型” 更好,是一種遞歸的模式尋找父加載器,一直找到頂部 bootstrap classloader,父加載不到就依次回溯到子加載器。


image.png

幾個(gè)類(lèi)加載器嚴(yán)格定義的父子委托關(guān)系,這樣可以保證每個(gè)類(lèi)只會(huì)被一個(gè) 類(lèi)加載器 只加載一次。

4. classloader 應(yīng)用 - 談?wù)劅岵渴?/h1>

之前做 java web 開(kāi)發(fā)的時(shí)候,修改了代碼,不能實(shí)時(shí)更新,很惱火,每次都要重新編譯打包運(yùn)行。
熱部署的本質(zhì),簡(jiǎn)單的理解,是在運(yùn)行中實(shí)時(shí)增加、替換 JVM 中的類(lèi)文件而無(wú)需重啟 JVM。
實(shí)時(shí)監(jiān)聽(tīng)
動(dòng)態(tài)替換

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容