目錄
Attach API:使用動態附件來滲透另外一個JVM
Instrumentation API:修改目标 VM 的程序
使用 Byte Buddy 来追踪内存泄漏
字节码操作
首頁 Java java教程 在Java中運用動態掛載實現Bug的熱修復的詳細解(圖)

在Java中運用動態掛載實現Bug的熱修復的詳細解(圖)

Mar 18, 2017 am 10:50 AM

大多數 JVM 具備 Java 的 HotSwap 特性,大部分開發者認為它只是一個調試工具。利用此特性,有可能在不重啟 Java 進程條件下,改變 Java 方法的實作。典型的例子是使用 IDE 來編碼。然而 HotSwap 可以在生產環境中實現這項功能。透過這種方式,不用停止運行程序,就可以擴展在線的應用程序,或者在運行的項目上修復小的錯誤。在這篇文章中,我將示範動態綁定、應用運行期程式碼變更進行綁定、介紹一些工具 API 以及 Byte Buddy 函式庫,這個函式庫提供了一些 API 程式碼變更更方便。

假設有一個正在運行的應用程序,透過校驗 HTTP 請求中的 X-Priority 頭部,來執行伺服器的特殊處理。該校驗使用下面的工具類別來實作:

class HeaderUtility {

    static boolean isPriorityCall(HttpServletRequest request) {
        return request.getHeader("X-Pirority") != null;
    }

}
登入後複製

你發現錯誤了嗎?這樣的錯誤很常見,尤其是在測試程式碼中常數值分解為靜態欄位重複使用。在不太理想的情況下,這個錯誤只會在產品被安裝的時候才被發現,其中頭透過另一個應用程式產生並沒有拼字錯誤。

修復這樣的錯誤並不難。在持續交付的時代,重新部署一個新的版本只需要點擊一下按鈕。但在其他情況下,變更可能就不是那麼簡單了,重新部署過程可能比較複雜,其中停機是不允許的,帶著錯誤運行可能會比較好。但 HotSwap 為我們提供了另一個選擇:在不重啟應用的前提下進行小幅改動。

Attach API:使用動態附件來滲透另外一個JVM

為了修改一個運行中的Java 程序,我們首先需要一個可以同處在運行狀態的JVM 進行通訊的方式。因為 Java 的虛擬機器實作是一個被管理的系統,因此擁有進行這些操作的標準 API。提問中涉及到的 API 被稱為 attachment API,它是官方 Java 工具的一部分。使用這個由運作之中的 JVM 所揭露的 API,能讓第二個 Java 進程來同其進行通訊。

事實上,我們已經使用了該 API: 它已經由諸如 VisualVM 或 Java Mission Control 這樣的除錯和模擬工具進行了應用。應用這些配件的API 並沒有同日常使用的標準Java API 打包在一起,而是被打包到了一個特殊的文件之中,叫做 tools.jar,它只包含了一個虛擬機的JDK打包發布版本。更糟的是,這個JAR 檔案的位置並沒有進行設置,它在Windows、Linux,特別是在Macintosh 上的VM 都存在差別,不光檔案的位置,連檔案名稱也各異,有些發行版上就被叫做 classes.jar。最後,IBM 甚至決定對這個 JAR 中包含的一些類別的名稱進行修改,將所有 com.sun 類別挪到 com.ibm 命名空間之中, 又添了一個亂子。在 Java 9 中,亂糟糟的狀態終於得以清理,tools.jar 被 Jigsaw 的模組 jdk.attach 所取代。

在 API 的 JAR (或模組) 進行了定位之後,我們就該讓其對附件程序可用。在 OpenJDK 上,被用來連接到另外一個 JVM 的類別叫做 VirtualMachine,它向任何由位於同一台實體機器上的 JDK 或是一個普通的 HtpSpot JVM 所運行的 VM 提供了一個入口點。在透過進程id 附加到另外一台虛擬機器上之後,我們就能夠在目標VM 指定的一個執行緒中執行一個JAR 檔案:

// the following strings must be provided by us
String processId = processId();
String jarFileName = jarFileName();
VirtualMachine virtualMachine = VirtualMachine.attach(processId);
try {
    virtualMachine.loadAgent(jarFileName, "World!");
} finally {
    virtualMachine.detach();
}
登入後複製

在收到一個JAR 檔案之後,目標虛擬機會查看該JAR 的程序清單描述檔(manifest),並定位處在 Premain-Class 屬性之下的類別。這非常類似於 VM 執行一個主方法的方式。有了一個Java 代理,VM 和指定的進程id 就可以查找到一個名為agentmain 的方法,該方法可以由指定執行緒中的遠端進程來執行:

public class HelloWorldAgent {

    public static void agentmain(String arg) {
        System.out.println("Hello, " + arg);
    }

}
登入後複製

使用該API,只要我們知道一個JVM 的進程id,就可以來在其上運行程式碼,列印出一條 Hello, World! 訊息。甚至有可能同並不熟 JDK 發行版一部分的 JVM 進行通信,只要附加的 VM 是用來存取 tools.jar 的 JDK 安裝程式。

Instrumentation API:修改目标 VM 的程序

到目前来看一切顺利。但是除了成功地同目标 VM 建立起了通信之外,我们还不能够修改目标 VM 上的代码以及 BUG。后续的修改,Java 代理可以定义第二参数来接收一个 Instrumentation 的实例 。稍后要实现的接口提供了向几个底层方法的访问途径,它们中的一个就能够对已经加载的代码进行修改。

为了修正 “X-Pirority” 错字,我们首先来假设为 HeaderUtility 引入了一个修复类,叫做 typo.fix,就在我们下面所开发的 BugFixAgent 后面的代理的 JAR 文件中。此外,我们需要给予代理通过向 manifest 文件添加 Can-Redefine-Classes: true 来替换现有类的能力。有了现在这些东西,我们就可以使用 instrumentation 的 API 来对类进行重新定义,该 API 会接受一对已经加载的类以及用来执行类重定义的字节数组

public class BugFixAgent {

    public static void agentmain(String arg, Instrumentation inst)
            throws Exception {
        // only if header utility is on the class path; otherwise,
        // a class can be found within any class loader by iterating
        // over the return value of Instrumentation::getAllLoadedClasses
        Class<?> headerUtility = Class.forName("HeaderUtility");

        // copy the contents of typo.fix into a byte array
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        try (InputStream input =
                BugFixAgent.class.getResourceAsStream("/typo.fix")) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = input.read(buffer)) != -1) {
                output.write(buffer, 0, length);
            }
        }

        // Apply the redefinition
        instrumentation.redefineClasses(
                new ClassDefinition(headerUtility, output.toByteArray()));
    }

}
登入後複製

运行上述代码后,HeaderUtility 类会被重定义以对应其修补的版本。对 isPrivileged 的任何后续调用现在将读取正确的头信息。作为一个小的附加说明,JVM 可能会在应用类重定义时执行完全的垃圾回收,并且会对受影响的代码进行重新优化。 总之,这会导致应用程序性能的短时下降。然而,在大多数情况下,这是较之完全重启进程更好的方式。

当应用代码更改时,要确保新类定义了与它替换的类完全相同的字段、方法和修饰符。 尝试修改任何此类属性的类重定义行为都会导致 UnsupportedOperationException。现在 HotSpot 团队正试图去掉这个限制。此外,基于 OpenJDK 的动态代码演变虚拟机支持预览此功能。

使用 Byte Buddy 来追踪内存泄漏

一个如上述示例的简单的 BUG 修复代理在你熟悉了 instrumentation 的 API 的时候是比较容易实现的。只要更加深入一点,也可以在运行代理的时候,无需手动创建附加的 class 文件,而是通过重写现有的 class 来应用更多通用的代码修改。

字节码操作

编译好的 Java 代码所呈现的是一系列字节码指令。从这个角度来看,一个 Java 方法无非就是一个字节数组,其每一个字节都是在表示一个向运行时发出的指令,或者是最近一个指令的参数。每个字节对应其意义的映射在《Java 虚拟机规范》中进行了定义,例如字节 0xB1 就是在指示 VM 从一个带有 void 返回类型的方法返回。因此,对字节码进行增强就是对一个方法的字节数字进行扩展,将我们想要应用的表示额外的业务逻辑指令包含进去。

当然,逐个字节的操作会特别麻烦,而且容易出错。为了避免手工的处理,许多的库都提供了更高级一点的 API,使用它们不需要我们直接同 Java 字节码打交道。这样的库其中就有一个叫做 Byte Buddy (当然我就是该库的作者)。它的功能之一就是能够定义可以在方法原来的代码之前和之后被执行的模板方法。

以上是在Java中運用動態掛載實現Bug的熱修復的詳細解(圖)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP的影響:網絡開發及以後 PHP的影響:網絡開發及以後 Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP:許多網站的基礎 PHP:許多網站的基礎 Apr 13, 2025 am 12:07 AM

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

See all articles