新聞中心
Java熱更新
在持續(xù)交付的時(shí)代,重新部署一個(gè)新的版本只需要點(diǎn)擊一下按鈕。但在有的情況下,重新部署過(guò)程可能比較復(fù)雜,停機(jī)是不被允許的。所以JVM提供了另外一種選擇:在不重啟應(yīng)用的前提下進(jìn)行小幅改動(dòng),又稱(chēng)熱更新。

創(chuàng)新互聯(lián)是一家專(zhuān)注于網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站與策劃設(shè)計(jì),烈山網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:烈山等地區(qū)。烈山做網(wǎng)站價(jià)格咨詢(xún):13518219792
對(duì)于某些大型的應(yīng)用來(lái)說(shuō),每次的重啟都需要花費(fèi)大量的時(shí)間成本,所以,如果能在不重啟虛擬機(jī)的情況下更新一個(gè)類(lèi),在某些業(yè)務(wù)場(chǎng)景下變得十分重要。比如很多腳本語(yǔ)言就支持熱替換,例如服務(wù)器端PHP,只要替換了PHP源文件,這種改動(dòng)就會(huì)立即生效,無(wú)需重啟服務(wù)器。
在Java開(kāi)發(fā)領(lǐng)域,熱更新一直是一個(gè)難以解決的問(wèn)題,目前的Java虛擬機(jī)只能實(shí)現(xiàn)方法級(jí)別的熱更新,對(duì)于整個(gè)類(lèi)的結(jié)構(gòu)修改,仍然需要重啟虛擬機(jī)。
熱更新的方法
Java熱更新一直不斷地改進(jìn)。
1.4開(kāi)始JPDA引入了hotSwap機(jī)制(JPDA Enhancements),實(shí)現(xiàn)了debug時(shí)的method body的動(dòng)態(tài)性。
1.5開(kāi)始通過(guò)JVMTI實(shí)現(xiàn)的java.lang.instrument(Java Platform SE 8)的premain方式,實(shí)現(xiàn)了agent方式的動(dòng)態(tài)性(JVM啟動(dòng)時(shí)指定agent)。
1.6增加了agentmain方式,實(shí)現(xiàn)了運(yùn)行時(shí)動(dòng)態(tài)性(通過(guò)The Attach API 綁定到具體VM)。其基本實(shí)現(xiàn)是通過(guò)JVMTI的retransformClass/redefineClass進(jìn)行函數(shù)體級(jí)別的字節(jié)碼更新,ASM、CGLib之類(lèi)基本都是圍繞這些在做動(dòng)態(tài)性。
1.定義不同的classloader
在了解JVM ClassLoader之后(可以點(diǎn)擊查看《Java類(lèi)加載及對(duì)象創(chuàng)建過(guò)程詳解》),可以通過(guò)定義不同的ClassLoader,監(jiān)聽(tīng)文件變化后,通過(guò)新的ClassLoader加載新文件,然后做好相應(yīng)的狀態(tài)恢復(fù),對(duì)舊ClassLoader進(jìn)行卸載等動(dòng)作。(舊classloader及加載的class類(lèi)在沒(méi)有實(shí)例引用的情況下,full gc時(shí)會(huì)被回收掉)
Tomcat的動(dòng)態(tài)部署就是監(jiān)聽(tīng)war變化,然后調(diào)用StandardContext.reload(),用新的WebContextClassLoader實(shí)例來(lái)加載war,然后初始化servlet來(lái)實(shí)現(xiàn)。類(lèi)似的實(shí)現(xiàn)還有OSGi等。
這種熱更新的流程如下:
重新加載類(lèi)的過(guò)程
2.agentmain
筆者的項(xiàng)目目前采用的這種形式,雖然筆者造過(guò)好多輪子,但筆者更看好Arthas這樣的開(kāi)源產(chǎn)品。。。
agentmain熱更新的原理
為了實(shí)現(xiàn)Java進(jìn)程A與進(jìn)程B之間的本地通信,熱更新的JVM進(jìn)程使用VirutalMachine.attach(pid)來(lái)連接需要熱更新的JVM進(jìn)程,然后使用virtualMachine.loadAgent加載自定義的agent(筆者查看了Arthas源碼,原理也大致相同)。這個(gè)通信通道成功建立之后,那么進(jìn)程A就能通知進(jìn)程B去執(zhí)行某些操作,從而達(dá)到監(jiān)控進(jìn)程B或者控制進(jìn)程B的某些行為的目的。如jstack、jmap等JDK自帶的工具,基本都是通過(guò)Attach機(jī)制去達(dá)成各自想要的目的的。
JVM啟動(dòng)的時(shí)候,在JVM內(nèi)部啟動(dòng)了一個(gè)監(jiān)聽(tīng)線(xiàn)程,這個(gè)線(xiàn)程的名字叫“Signal Dispatcher”,該線(xiàn)程的作用是,監(jiān)聽(tīng)并處理OS的信號(hào)。
信號(hào)是一種進(jìn)程通信。如平常我們用的最多的就是 kill -9 ${pid}來(lái)殺死某個(gè)進(jìn)程,kill進(jìn)程通過(guò)向${pid}的進(jìn)程發(fā)送一個(gè)編號(hào)為“9”號(hào)的信號(hào),來(lái)通知系統(tǒng)強(qiáng)制結(jié)束${pid}的生命周期。)
至于attach實(shí)現(xiàn),在Linux下時(shí)使用文件Socket進(jìn)行進(jìn)程通信(對(duì)同一個(gè)文件進(jìn)行讀寫(xiě)操作,以達(dá)到信息的交互和共享)。
更詳細(xì)的原理,JVM大神寒泉子有篇文章《JVM源碼分析之javaagent原理完全解讀》,如點(diǎn)擊無(wú)法跳轉(zhuǎn),請(qǐng)查看筆者CSDN博客原文來(lái)點(diǎn)擊超鏈接。
3.Arthas
Arthas是阿里巴巴最近開(kāi)源出來(lái)的一個(gè)針對(duì)java的工具,主要是針對(duì)java的問(wèn)題進(jìn)行診斷。
跳轉(zhuǎn)官網(wǎng)地址
這個(gè)工具可以協(xié)助完成下面這些事情(轉(zhuǎn)自官網(wǎng)):
- 這個(gè)類(lèi)是從哪個(gè)jar包加載而來(lái)的?
- 為什么會(huì)報(bào)各種類(lèi)相關(guān)的Exception?
- 線(xiàn)上遇到問(wèn)題無(wú)法debug好蛋疼,難道只能反復(fù)通過(guò)增加System.out或通過(guò)加日志再重新發(fā)布嗎?
- 線(xiàn)上的代碼為什么沒(méi)有執(zhí)行到這里?是由于代碼沒(méi)有commit?還是搞錯(cuò)了分支?
- 線(xiàn)上遇到某個(gè)用戶(hù)的數(shù)據(jù)處理有問(wèn)題,但線(xiàn)上同樣無(wú)法 debug,線(xiàn)下無(wú)法重現(xiàn)。
- 是否有一個(gè)全局視角來(lái)查看系統(tǒng)的運(yùn)行狀況?
- 有什么辦法可以監(jiān)控到JVM的實(shí)時(shí)運(yùn)行狀態(tài)?
Arthas采用命令行交互模式,同時(shí)提供豐富的Tab自動(dòng)補(bǔ)全功能,進(jìn)一步方便進(jìn)行問(wèn)題的定位和診斷。
Arthas提供在線(xiàn)教程,相比一般的開(kāi)源產(chǎn)品,上手真的很贊。
arthas實(shí)現(xiàn)熱更新
使用Arthas三個(gè)命令就可以搞定熱更新
- jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
- mc /tmp/UserController.java -d /tmp
- redefine /tmp/com/example/demo/arthas/user/UserController.class
- jad命令反編譯,然后可以用其它編譯器,比如vim來(lái)修改源碼
- mc命令來(lái)內(nèi)存編譯修改過(guò)的代碼
- 用redefine命令加載新的字節(jié)碼
JVM熱更新的局限
基于Attach機(jī)制實(shí)現(xiàn)的熱更新,更新類(lèi)需要與原來(lái)的類(lèi)在包名,類(lèi)名,修飾符上完全一致,否則在classRedefine過(guò)程中會(huì)產(chǎn)生classname don't match 的異常。
例如顯示這樣的報(bào)錯(cuò):redefineClasses exception class redefinition failed: attempted to delete a method.
具體來(lái)說(shuō),JVM熱更新局限總結(jié):
- 函數(shù)參數(shù)格式不能修改,只能修改函數(shù)內(nèi)部的邏輯
- 不能增加類(lèi)的函數(shù)或變量
- 函數(shù)必須能夠退出,如果有函數(shù)在死循環(huán)中,無(wú)法執(zhí)行更新類(lèi)(筆者實(shí)驗(yàn)發(fā)現(xiàn),死循環(huán)跳出之后,再執(zhí)行類(lèi)的時(shí)候,才會(huì)是更新類(lèi))
最后,限于筆者經(jīng)驗(yàn)水平有限,歡迎讀者就文中的觀(guān)點(diǎn)提出寶貴的建議和意見(jiàn)。
網(wǎng)頁(yè)名稱(chēng):一文搞定Java熱更新
文章轉(zhuǎn)載:http://www.5511xx.com/article/copchph.html


咨詢(xún)
建站咨詢(xún)
