一篇文章解密 Arthas 實現原理
前言
在之前文章中介紹了 Arthas 應用診斷利器--入門和常用騷操作,想必大家同我一樣對 Arthas 這么強大的功能所折服(如何做到無需重啟 attach 到 JVM、又如何實現各種監聽和統計等功能),今天我們就來對 Arthash 的實現進行解密。提前透露下今天重要的角色:Instrument、ASM。
Instrument
帶著問題 Arthas 如何做到無需重啟 attach 到 JVM 開始進入正題,首先先介紹下 Instrument。
Instrumentation類提供控制Java語言程序代碼的服務。Instrumentation可以實現在方法插入額外的字節碼從而達到收集使用中的數據到指定工具的目的。由于插入的字節碼是附加的,這些更變不會修改原來程序的狀態或者行為。通過這種方式實現的良性工具包括監控代理、分析器、覆蓋分析程序和事件日志記錄程序等等。
簡單來說,Instrument 就是「針對已有的類修改其字節碼來增強其邏輯,從開發者的角度可以理解為 JVM 層面的 AOP 編程」。開源的很多 APM(Application Performance Monitor) 框架如 SkyWalking、PinPoint 等都是通過java.lang.instrument包提供的字節碼增強功能來實現的,大部分情況下 我們都是使用 Instrument 字節碼插樁的功能。
- Jdk5 開始引入 java.lang.instrument 包,一開始只有 premain 的方式(通過命令行使用外部代理jar包 )
- 新建/在現有的項目中,編寫 premain 函數 public static void premain(String agentArgs, Instrumentation inst)。
- 將項目打成 jar 包,并引入 Maven 插件 maven-jar-plugin 指定 Premain-Class。
通過指定Agent運行 java -javaagent:代理Jar包的路徑 [=傳入premain的參數] yourTarget.jar
Jdk6 之后針對這點進行優化,不再需要在通過命令 -javaagent 的方式指定引入代理 Jar,而是通過使用 agentmain 在運行時通過attach工具激活指定代理。就可以通過 addTransformer,retransformClasses,redefineClasses等方式對字節碼進行增強和熱替換了。
- 新建/在現有的項目中,編寫 agentmain 函數 public static void agentmain(String agentArgs, Instrumentation inst)。
- 將項目打成 jar 包,并引入 Maven 插件 maven-jar-plugin 指定 Premain-Class。
- 通過attach工具直接加載Agent。
「簡單的提下 Instrument原理:」
instrument 的底層實現依賴于 JVMTI(JVM Tool Interface),它是JVM暴露出來的一些供用戶擴展的接口集合,JVMTI是基于事件驅動的, JVM 每執行到一定的邏輯就會調用一些事件的回調接口(如果有的話),這些接口可以供開發者去擴展自己的邏輯。JVMTIAgent 是一個利用 JVMTI 暴露出來的接口提供了代理啟動時加載(agent on load)、代理通過 attach 形式加載(agent on attach)和代理卸載(agent on unload)功能的動態庫。而instrument agent可以理解為一類 JVMTIAgent 動態庫,別名是 JPLISAgent(Java Programming Language Instrumentation Services Agent),也就是專門為java語言編寫的插樁服務提供支持的代理。
ASM
既然已經有了重寫類的入口(Instrument),那么只需要結合第三方的字節碼編譯工具即可完成想要的功能了,Arthas 就是通過 ASM 用來動態生成class或者增強class,比如常用的 Gradle 在運行時基于 ASM 運行時生成一些類、 CGLib 也是基于 ASM 實現的(插一個題外話:Jdk Proxy而是基于是「反射機制」實現的)
「ASM」是一個通用的 Java 字節碼操作和分析框架。它可用于直接以二進制形式修改現有類或動態生成類。ASM 提供了一些常見的字節碼轉換和分析算法,可以從中構建自定義的復雜轉換和代碼分析工具。ASM 可以直接產生二進制 class 文件,也可以在類被加載入 Java 虛擬機之前動態改變類行為。Java class 被存儲在嚴格格式定義的 .class文件里,這些類文件擁有足夠的元數據來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節碼(指令)。ASM從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據用戶要求生成新類。
ASM 提供與其他 Java 字節碼框架類似的功能,但側重于 性能。因為它被「設計和實現得盡可能小和盡可能快」,所以它「非常適合在動態系統中使用」(但當然也可以以靜態方式使用,例如在編譯器中)。ASM 字節碼增強技術主要是用來反射的時候提升性能的,如果單純用jdk的反射調用,性能是非常低下的,而使用字節碼增強技術后反射調用的時間已經基本可以與直接調用相當。
ASM:
https://asm.ow2.io/index.html。
「ASM 字節碼處理流程:」目標類 class bytes -> ClassReader解析 -> ClassVisitor增強修改字節碼 -> ClassWriter生成增強后的 class bytes。
「Arthas 如何做到無需重啟 attach 到 JVM (ASM + Instrument 處理流程):」
目標類 class bytes -> ClassReader解析 -> ClassVisitor增強修改字節碼 -> ClassWriter生成增強后的 class bytes -> 通過Instrument解析加載為新的Class.