【效率專精系列】幾種常見的JVM熱部署技術及實現難點淺談

開發、自測、聯調期間代碼可能會被頻繁地修改,一般即便只增長了一行代碼,都須要重啓容器以檢查執行效果。而熱部署技術可以幫助開發人員減小從新部署的等待時間。本文的目的爲調研熱部署的技術現狀及其對開發效率的幫助,並簡單梳理其技術實現的難點。html

clipboard.png

熱部署技術

總結

JVM熱部署目前有多種技術實現:官方、開源、商業。其中商業的JRebel功能強大,涵蓋了平常開發中大部分熱更新場景。以團隊中一個基於Tomcat + Spring的業務後臺爲例,修改代碼後,本地冷啓動耗時4.5min,本地熱部署的時間則小於1s,極大改善了開發效率。java

官方實現

當前JVM和JVMTI(JVM Tool Interface)規範中經過相應的agent機制支持的retransformClass/redefineClass操做能夠在加載前和加載後動態修改類的內容,從Java 5開始,這一功能還經過Instrumentation API直接提供給Java應用使用,可是其適用範圍是受限的:只能修改已有方法的方法體。git

如下摘自 JVM(TM) Tool Interface 1.2.3
The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, change modifiers, or change inheritance. These restrictions may be lifted in future versions. See the error return description below for information on error codes returned if an unsupported redefinition is attempted.

IDE的edit-and-continue功能(Intellij IdeaUpdate Application)就用到了這種被稱爲HotSwap的熱部署技術,可是它的限制太大,徹底沒法知足實際開發中的需求。github

Dynamic Code Evolution VM(DCEVM)

這是一個由JKU主導的、基於HotSpot VM的研究項目,誕生於2010年。該項目但願能動態修改類的任意元素,包括成員、方法、註解、繼承等而無需重啓JVM。目前的light版已經支持到Java 8 update 144, build 2mvc

HotSwapAgent

基於DCEVM構建的開源項目,其完成度要高於DCEVM,目前已發佈1.0版。對於常見的IDE、IoC/ORM/Log框架、J2EE應用容器的支持比較完善。根據官方文檔,HA支持下列特性。intellij-idea

  • Add/remove/modify class fields.
  • Add/remove/modify methods. Add/remove/modify method annotations
  • Add/remove/modify classes including anonymous classes. HotswapAgent handless correct anonymous class redefinitions.
  • Add/remove static member of classes. HotswapAgent handles static member initialization.
  • Add/remove enum values
  • Refresh framework and application server settings

JRebel

Java世界中大名鼎鼎的熱部署解決方案,熱部署特性與上面提到的HotSwapAgent相似。固然做爲一款商業軟件,它支持的框架、IDE、J2EE應用容器的種類都更多,總計100+;同時支持Hotspot VM和Oracle VM;文檔和社區支持很是完善,很容易上手。app

最重要的是,沒錢的碼農能夠經過贊助官方的Social Plan免費激活JRebel!cors

JRebel實測

測試環境爲團隊使用的Tomcat + Spring + SpringMvc
如下是實際開發中常見的改動類型的測試結果。【Pass】爲支持,【Fail】爲不支持。框架

  • 【Pass】在Spring和SpringMvc的配置Xml中增長Bean定義
  • 【Pass】新增Controller類,新增、修改Controller註解
  • 【Pass】新增、修改RequestHandler方法、方法體、方法簽名、註解
  • 【Fail】在Spring和SpringMvc的配置Xml中增長、修改容器配置項。如<mvc:interceptors> / <mvc:cors> / <aop:aspectj-autoproxy> / <mvc:async-support>

淺談熱部署的實現難點

熱部署的本質,簡單的理解,是在運行中實時增長、替換JVM中的類文件而無需重啓JVM。less

衆所周知,JVM使用ClassLoader加載類文件,內含的雙親委派模型經過指定類文件加載的順序避免因爲類衝突而致使核心類庫加載失敗。單個ClassLoader不能加載全限定名相同的類;不能修改已加載的類的聲明;不能卸載已加載的類,除非移除整個ClassLoader,或者被GC回收。

那麼,修改原有的類(如Test.class)的任意元素後,熱部署就會面臨不少問題。比方說:

  1. 兩個全限定名相同的類如何加載?
  2. 類的實例化如何獲取到新的類?
  3. 更新類的聲明後,如增長類方法、實例方法;修改類方法、實例方法簽名、方法體、方法註解;新增、修改類變量、實例變量;修改接口、類的繼承關係,調用點怎麼指向新的類?
  4. 若是JVM使用了內聯優化技術呢?
  5. 如何保證反射正確,好比調用ClassgetName()getMethods()getField()等方法時如何獲取到新的類?
  6. 若是用了容器或者框架,修改JavaConfig或者XML後,怎麼反映到容器裏?

熱部署問題在底層繞不開ClassLoader,當一個類被更新後,須要被從新載入到ClassLoader中,原先對類變量、實例變量、類方法、實例方法的調用都須要重定向到新類。能夠經過引入一個包含全部符號連接的中間層,當JVM加載用戶的類時進行動態加強,並記錄下涉及的符號連接。

舉幾個實現思路的小例子:

  1. 在類加載時爲新的類起一個新的限定名(如原來的類名是Test,而新的類名是Test_v1),繞開ClassLoader的限制。
  2. 全部指向老的類的符號連接都實時替換爲相應的新符號連接
  3. 在ClassLoader中查找老的類時返回新的類
  4. 監控容器配置文件,發生變化後調用容器refresh API

Reference

  1. Java SE 6 新特性 Instrumentation 新功能
  2. HotSwapAgent
  3. 深刻探索 Java 熱部署
  4. DCEVM
  5. Get True Hot Swap in Java with DCEVM and IntelliJ IDEA
  6. Features - JRebel
  7. HotSwap和JRebel原理
  8. 實現加強的java class hotswap (三) 解決方案 續
  9. 實現加強的java class hotswap (三) 解決方案
  10. Java是否能夠作到修改類而不用重啓JVM?
相關文章
相關標籤/搜索