<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                本課時我們主要分析如何使用 Java Agent 技術對字節碼進行修改。 Java 5 版本以后,JDK 有一個包叫做 instrument ,能夠實現一些非常酷的功能,市面上一些 APM 工具,就是通過它來進行的增強,這個功能對于業務開發者來說,是比較偏門的。但你可能在無意中已經用到它了,比如 Jrebel 酷炫的熱部署功能(這個工具能夠顯著增加開發效率)。 本課時將以一個例子來看一下具體的應用場景,然后介紹一個在線上常用的問題排查工具:Arthas。 #### Java Agent 介紹 我們上面說的這些工具的基礎,就是 Java Agent 技術,可以利用它來構建一個附加的代理程序,用來協助檢測性能,還可以替換一些現有功能,甚至 JDK 的一些類我們也能修改,有點像 JVM 級別的 AOP 功能。 通常,Java 入口是一個 main 方法,這是毋庸置疑的,而 Java Agent 的入口方法叫做 premain,表明是在 main 運行之前的一些操作。Java Agent 就是這樣的一個 jar 包,定義了一個標準的入口方法,它并不需要繼承或者實現任何其他的類,屬于無侵入的一種開發模式。 > 為什么叫 premain?這是一個約定,并沒有什么其他的理由,這個方法,無論是第一次加載,還是每次新的 ClassLoader 加載,都會執行。 我們可以在這個前置的方法里,對字節碼進行一些修改,來增加功能或者改變代碼的行為,這種方法沒有侵入性,只需要在啟動命令中加上 -javaagent 參數就可以了。Java 6 以后,甚至可以通過 attach 的方式,動態的給運行中的程序設置加載代理類。 其實,instrument 一共有兩個 main 方法,一個是 premain,另一個是 agentmain,但在一個 JVM 中,只會調用一個;前者是 main 執行之前的修改,后者是控制類運行時的行為。它們還是有一些區別的,agentmain 因為能夠動態修改大部分代碼,比較危險,限制會更大一些。 #### 有什么用 * [ ] 獲取統計信息 在許多 APM 產品里,比如 Pinpoint、SkyWalking 等,就是使用 Java Agent 對代碼進行的增強。通過在方法執行前后動態加入的統計代碼,來進行監控信息的收集;通過兼容 OpenTracing 協議,可以實現分布式鏈路追蹤的功能。 它的原理類似于 AOP,最終以字節碼的形式存在,性能損失取決于你的代碼邏輯。 * [ ] 熱部署 通過自定義的 ClassLoader,可以實現代碼的熱替換。使用 agentmain,實現熱部署功能會更加便捷,通過 agentmain 獲取到 Instrumentation 以后,就可以對類進行動態重定義了。 * [ ] 診斷 配合 JVMTI 技術,可以 attach 到某個進程進行運行時的統計和調試,比較流行的 btrace 和 arthas ,其底層就是這種技術。 * [ ] 代碼示例 要構建一個 agent 程序,大體可分為以下步驟: * 使用字節碼增強工具,編寫增強代碼; * 在 manifest 中指定 Premain-Class/Agent-Class 屬性; * 使用參數加載或者使用 attach 方式。 我們來詳細介紹一下這個過程。 #### 編寫 Agent Java Agent 最終的體現方式是一個 jar 包,使用 IDEA 創建一個默認的 maven 工程即可。 創建一個普通的 Java 類,添加 premain 或者 agentmain 方法,它們的參數完全一樣。 ![](https://img.kancloud.cn/99/96/999679ea9ff73eb8bda65a9ae3d36447_830x341.jpg) #### 編寫 Transformer 實際的代碼邏輯需要實現 ClassFileTransformer 接口。假如我們要統計某個方法的執行時間,使用 JavaAssist 工具來增強字節碼,則可以通過以下代碼來實現: * 獲取 MainRun 類的字節碼實例; * 獲取 hello 方法的字節碼實例; * 在方法前后,加入時間統計,首先定義變量 _begin,然后追加要寫的代碼。 別忘了加入 maven 依賴,我們借用 javassist 完成字節碼增強: ``` <dependency> ????<groupId>org.javassist</groupId> ????<artifactId>javassist</artifactId> ????<version>3.24.1-GA</version> </dependency> ``` ![](https://img.kancloud.cn/a7/ed/a7ed251e11a384282867aa226c72db13_889x667.jpg) 字節碼增強也可以使用 Cglib、ASM 等其他工具。 * [ ] MANIFEST.MF 文件 那么我們編寫的代碼是如何讓外界知曉的呢?那就是依靠 MANIFEST.MF 文件,具體路徑在 src/main/resources/META-INF/MANIFEST.MF: ``` Manifest-Version:?1.0 premain-class:?com.sayhiai.example.javaagent.AgentApp ``` 一般的,maven 打包會覆蓋這個文件,所以我們需要為它指定一個。 ``` <build><plugins><plugin> <groupId>org.apache.maven.plugins</groupId> ????<artifactId>maven-jar-plugin</artifactId> ????<configuration> ????????<archive> ????????????<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> ????????????</archive> ????</configuration></plugin></plugins></build> ``` 然后,在命令行,執行 mvn install 安裝到本地代碼庫,或者使用 mvn deploy 發布到私服上。 附 MANIFEST.MF 參數清單: ``` Premain-Class Agent-Class Boot-Class-Path Can-Redefine-Classes Can-Retransform-Classes Can-Set-Native-Method-Prefix ``` * [ ] 使用 使用方式取決于你使用的 premain 還是 agentmain,它們之間有一些區別,具體如下。 * [ ] premain 在我們的例子中,直接在啟動命令行中加入參數即可,在 jvm 啟動時啟用代理。 ``` java?-javaagent:agent.jar?MainRun ``` 在 IDEA 中,可以將參數附著在 jvm options 里。 ![](https://img.kancloud.cn/29/1c/291c848bfb6ad560a987479ba9132e50_750x456.jpg) 接下來看一下測試代碼。 ![](https://img.kancloud.cn/ac/12/ac1228fbacfa116e011bcd1aaac6ead3_706x347.jpg) 這是我們的執行類,執行后,直接輸出 hello world。通過增強以后,還額外的輸出了執行時間,以及一些 debug 信息。其中,debug 信息在 main 方法執行之前輸出。 ![](https://img.kancloud.cn/99/8a/998a73a94b61e851fc50b68821f9b43b_616x163.jpg) * [ ] agentmain 這種模式一般用在一些診斷工具上。使用 jdk/lib/tools.jar 中的工具類,可以動態的為運行中的程序加入一些功能。它的主要運行步驟如下: * 獲取機器上運行的所有 JVM 進程 ID; * 選擇要診斷的 jvm; * 將 jvm 使用 attach 函數鏈接上; * 使用 loadAgent 函數加載 agent,動態修改字節碼; * 卸載 jvm。 代碼樣例如下: ``` import?com.sun.tools.attach.VirtualMachine; import?com.sun.tools.attach.VirtualMachineDescriptor; import?java.util.List; public?class?JvmAttach?{ ????public?static?void?main(String[]?args) ????????????throws?Exception?{ ????????List<VirtualMachineDescriptor>?list?=?VirtualMachine.list(); ????????for?(VirtualMachineDescriptor?vmd?:?list)?{ ????????????if?(vmd.displayName().endsWith("MainRun"))?{ ????????????????VirtualMachine?virtualMachine?=?VirtualMachine.attach(vmd.id()); ????????????????virtualMachine.loadAgent("test.jar?",?"..."); ????????????????//..... ????????????????virtualMachine.detach(); ????????????} ????????} ????} ``` 這些代碼功能雖然強大,但都是比較危險的,這就是為什么 Btrace 說了這么多年,還是只在小范圍內被小心的使用。相對來說,Arthas 顯的友好而且安全的多。 使用注意點 (1)jar 包依賴方式 一般,Agent 的 jar 包會以 fatjar 的方式提供,即將所有的依賴打包到一個大的 jar 包中。如果你的功能復雜、依賴多,那么這個 jar 包將會特別的大。 使用獨立的 bom 文件維護這些依賴是另外一種方法。使用方自行管理依賴問題,但這通常會發生一些找不到 jar 包的錯誤,更糟糕的是,大多數在運行時才發現。 (2)類名稱重復 不要使用和 jdk 及 instrument 包中相同的類名(包括包名),有時候你能夠僥幸過關,但也會陷入無法控制的異常中。 (3)做有限的功能 可以看到,給系統動態的增加功能是非常酷的,但大多數情況下非常耗費性能。你會發現,一些簡單的診斷工具,會占用你 1 核的 CPU,這是很平常的事情。 (4)ClassLoader 如果你用的 JVM 比較舊,頻繁地生成大量的代理類,會造成元空間的膨脹,容易發生內存占用問題。 ClassLoader 有雙親委派機制,如果你想要替換相應的類,一定要搞清楚它的類加載器應該用哪個,否則替換的類,是不生效的。 具體的調試方法,可以在 Java 進程啟動時,加入 -verbose:class 參數,用來監視引用程序對類的加載。 #### Arthas 我們來回顧一下在故障排查時所做的一些準備和工具支持。 在第 09 課時,我們了解了 jstat 工具,還有 jmap 等查看內存狀態的工具;第 11 課時,介紹了超過 20 個工具的使用,這需要開發和分析的人員具有較高的水平;第 15 課時,還介紹了 jstack 的一些典型狀態。對于這種瞬時態問題的分析,需要綜合很多工具,對剛進入這個行業的人來說,很不友好。 Arthas 就是使用 Java Agent 技術編寫的一個工具,具體采用的方式,就是我們上面提到的 attach 方式,它會無侵入的 attach 到具體的執行進程上,方便進行問題分析。 你甚至可以像 debug 本地的 Java 代碼一樣,觀測到方法執行的參數值,甚至做一些統計分析。這通常可以解決下面的問題: * 哪個線程使用了最多的 CPU * 運行中是否有死鎖,是否有阻塞 * 如何監測一個方法哪里耗時最高 * 追加打印一些 debug 信息 * 監測 JVM 的實時運行狀態 [Arthas 官方文檔十分詳細,也可以點擊這里參考](https://alibaba.github.io/arthas)。 但無論工具如何強大,一些基礎知識是需要牢固掌握的,否則,工具中出現的那些術語,也會讓人一頭霧水。 工具常變,但基礎更加重要。如果你想要一個適應性更強的技術棧,還是要多花點時間在原始的排查方法上。 #### 小結 本課時介紹了開發人員極少接觸的 Java Agent 技術,但在平常的工作中你可能不知不覺就用到它了。在平常的面試中,一些面試官也會經常問一些相關的問題,以此來判斷你對整個 Java 體系的掌握程度,如果你能回答上來,說明你已經脫穎而出了。 值得注意的是,這個知識點,對于做基礎架構(比如中間件研發)的人來說,是必備技能,如果不了解,那面試可能就要涼了。 從實用角度來說,阿里開源的 Arthas 工具,是非常好用的,如果你有線上的運維權限,不妨嘗試一下。 [本課時項目代碼,可點擊這里查看](https://gitee.com/xjjdog/jvm-lagou-res/tree/master/jvm-21/javaagent-demo)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看