搜尋
首頁Javajava教程在Java中運用動態掛載實現Bug的熱修復的詳細解(圖)

大多數 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
如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?Mar 17, 2025 pm 05:46 PM

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?Mar 17, 2025 pm 05:44 PM

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?Mar 17, 2025 pm 05:43 PM

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Mar 17, 2025 pm 05:35 PM

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。