日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

詳解Java動態字節碼技術

瀏覽:31日期:2022-08-11 16:49:39
目錄對 Debug 的好奇ASM動態生成字節碼ASM 框架常用方法InstrumentJVM TI介紹Agent使用agent生成代碼實現被修改的類AgentAttacher小結對 Debug 的好奇

初學 Java 時,我對 IDEA 的 Debug 非常好奇,不止是它能查看斷點的上下文環境,更神奇的是我可以在斷點處使用它的 Evaluate 功能直接執行某些命令,進行一些計算或改變當前變量。

剛開始語法不熟經常寫錯代碼,重新打包部署一次代碼耗時很長,我就直接面向 Debug 開發。在要編寫的方法開始處打一個斷點,在 Evaluate 框內一次次地執行方法函數不停地調整代碼,沒問題后再將代碼復制出來放到 IDEA 里,再進行下一個方法的編寫,這樣就跟寫 PHP 類似的解釋性語言一樣,寫完即執行,非常方便。

詳解Java動態字節碼技術

但 Java 是靜態語言,運行之前是要先進行編譯的,難道我寫的這些代碼是被實時編譯又”注入”到我正在 Debug 的服務里了嗎?

隨著對 Java 的愈加熟悉,我也了解了反射、字節碼等技術,直到前些天的周會分享,有位同事分享了 Btrace 的使用和實現,提到了 Java 的 ASM 框架和 JVM TI 接口。 Btrace 修改代碼能力的實現與 Debug 的 Evaluate 有很多相似之處,這大大吸引了我。分享就像一個引子,從中學到的東西只是皮毛,要了解它還是要自己研究。于是自己查看資料并寫代碼學習了下其具體實現。

ASM

實現 Evaluate 要解決的第一個問題就是怎么改變原有代碼的行為,它的實現在 Java 里被稱為動態字節碼技術。

動態生成字節碼

我們知道,我們編寫的 Java 代碼都是要被編譯成字節碼后才能放到 JVM 里執行的,而字節碼一旦被加載到虛擬機中,就可以被解釋執行。

字節碼文件(.class)就是普通的二進制文件,它是通過 Java 編譯器生成的。而只要是文件就可以被改變,如果我們用特定的規則解析了原有的字節碼文件,對它進行修改或者干脆重新定義,這不就可以改變代碼行為了么。

Java 生態里有很多可以動態生成字節碼的技術,像 BCEL、Javassist、ASM、CGLib 等,它們各有自己的優勢。有的使用復雜卻功能強大、有的簡單確也性能些差。

ASM 框架

ASM 是它們中最強大的一個,使用它可以動態修改類、方法,甚至可以重新定義類,連 CGLib 底層都是用 ASM 實現的。

當然,它的使用門檻也很高,使用它需要對 Java 的字節碼文件有所了解,熟悉 JVM 的編譯指令。雖然我對 JVM 的字節碼語法不熟,但有大神開發了可以在 IDEA 里查看字節碼的插件:ASM Bytecode Outline,在要查看的類文件里右鍵選擇Show bytecode Outline即可以右側的工具欄查看我們要生成的字節碼。對照著示例,我們就可以很輕松地寫出操作字節碼的 Java 代碼了。

而切到ASMified標簽欄,我們甚至可以直接獲取到 ASM 的使用代碼。

詳解Java動態字節碼技術

常用方法

在 ASM 的代碼實現里,最明顯的就是訪問者模式,ASM 將對代碼的讀取和操作都包裝成一個訪問者,在解析 JVM 加載到的字節碼時調用。

ClassReader 是 ASM 代碼的入口,通過它解析二進制字節碼,實例化時它時,我們需要傳入一個 ClassVisitor,在這個 Visitor 里,我們可以實現visitMethod()/visitAnnotation()等方法,用以定義對類結構(如方法、字段、注解)的訪問方法。

而 ClassWriter 接口繼承了 ClassVisitor 接口,我們在實例化類訪問器時,將 ClassWriter “注入” 到里面,以實現對類寫入的聲明。

Instrument

介紹

字節碼是修改完了,可是 JVM 在執行時會使用自己的類加載器加載字節碼文件,加載后并不會理會我們做出的修改,要想實現對現有類的修改,我們還需要搭配 Java 的另一個庫instrument。

instrument 是 JVM 提供的一個可以修改已加載類文件的類庫。1.6以前,instrument 只能在 JVM 剛啟動開始加載類時生效,之后,instrument 更是支持了在運行時對類定義的修改。

使用

要使用 instrument 的類修改功能,我們需要實現它的ClassFileTransformer接口定義一個類文件轉換器。它唯一的一個transform()方法會在類文件被加載時調用,在 transform 方法里,我們可以對傳入的二進制字節碼進行改寫或替換,生成新的字節碼數組后返回,JVM 會使用 transform 方法返回的字節碼數據進行類的加載。

JVM TI

定義完了字節碼的修改和重定義方法,但我們怎么才能讓 JVM 能夠調用我們提供的類轉換器呢?這里又要介紹到 JVM TI 了。

介紹

JVM TI(JVM Tool Interface)JVM 工具接口是 JVM 提供的一個非常強大的對 JVM 操作的工具接口,通過這個接口,我們可以實現對 JVM 多種組件的操作,從JVMTM Tool Interface這里我們認識到 JVM TI 的強大,它包括了對虛擬機堆內存、類、線程等各個方面的管理接口。

JVM TI 通過事件機制,通過接口注冊各種事件勾子,在 JVM 事件觸發時同時觸發預定義的勾子,以實現對各個 JVM 事件的感知和反應。

Agent

Agent 是 JVM TI 實現的一種方式。我們在編譯 C 項目里鏈接靜態庫,將靜態庫的功能注入到項目里,從而才可以在項目里引用庫里的函數。我們可以將 agent 類比為 C 里的靜態庫,我們也可以用 C 或 C++ 來實現,將其編譯為 dll 或 so 文件,在啟動 JVM 時啟動。

這時再來思考 Debug 的實現,我們在啟動被 Debug 的 JVM 時,必須添加參數-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:3333,而 -agentlib 選項就指定了我們要加載的 Java Agent,jdwp 是 agent 的名字,在 linux 系統中,我們可以在 jre 目錄下找到 jdwp.so 庫文件。

Java 的調試體系 jdpa 組成,從高到低分別為jdi->jdwp->jvmti,我們通過 JDI 接口發送調試指令,而 jdwp 就相當于一個通道,幫我們翻譯 JDI 指令到 JVM TI,最底層的 JVM TI 最終實現對 JVM 的操作。

使用

JVM TI 的 agent 使用很簡單,在啟動 agent 時添加 -agent 參數指定我們要加載的 agent jar包即可。

而要實現代碼的修改,我們需要實現一個 instrument agent,它可以通過在一個類里添加premain()或agentmain()方法來實現。而要實現 1.6 以上的動態 instrument 功能,實現 agentmain 方法即可。

在 agentmain 方法里,我們調用Instrumentation.retransformClasses()方法實現對目標類的重定義。

另外往一個正在運行的 JVM 里動態添加 agent,還需要用到 JVM 的 attach 功能,Sun 公司的 tools.jar 包里包含的VirtualMachine類提供了 attach 一個本地 JVM 的功能,它需要我們傳入一個本地 JVM 的 pid, tools.jar 可以在 jre 目錄下找到。

agent生成

另外,我們還需要注意 agent 的打包,它需要指定一個 Agent-Class 參數指定我們的包括 agentmain 方法的類,可以算是指定入口類吧。

此外,還需要配置MANIFEST.MF文件的一些參數,允許我們重新定義類。如果你的 agent 實現還需要引用一些其他類庫時,還需要將這些類庫都打包到此 jar 包中,下面是我的 pom 文件配置。

<build> <plugins><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration><archive> <manifestEntries><Agent-Class>asm.TestAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes><Manifest-Version>1.0</Manifest-Version><Permissions>all-permissions</Permissions> </manifestEntries></archive><descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs> </configuration></plugin> </plugins></build>

另外在打包時需要使用mvn assembly:assembl命令生成 jar-with-dependencies 作為 agent。

代碼實現

我在測試時寫了一個用以上技術實現了一個簡單的字節碼動態修改的 Demo。

被修改的類

TransformTarget 是要被修改的目標類,正常執行時,它會三秒輸出一次 “hello”。

public class TransformTarget { public static void main(String[] args) {while (true) { try {Thread.sleep(3000L); } catch (Exception e) {break; } printSomething();} } public static void printSomething() {System.out.println('hello'); }}Agent

Agent 是執行修改類的主體,它使用 ASM 修改 TransformTarget 類的方法,并使用 instrument 包將修改提交給 JVM。

入口類,也是代理的 Agent-Class。

public class TestAgent { public static void agentmain(String args, Instrumentation inst) {inst.addTransformer(new TestTransformer(), true);try { inst.retransformClasses(TransformTarget.class); System.out.println('Agent Load Done.');} catch (Exception e) { System.out.println('agent load failed!');} }}

執行字節碼修改和轉換的類。

public class TestTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {System.out.println('Transforming ' + className);ClassReader reader = new ClassReader(classfileBuffer);ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);ClassVisitor classVisitor = new TestClassVisitor(Opcodes.ASM5, classWriter);reader.accept(classVisitor, ClassReader.SKIP_DEBUG);return classWriter.toByteArray(); } class TestClassVisitor extends ClassVisitor implements Opcodes {TestClassVisitor(int api, ClassVisitor classVisitor) { super(api, classVisitor);}@Overridepublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (name.equals('printSomething')) {mv.visitCode();Label l0 = new Label();mv.visitLabel(l0);mv.visitLineNumber(19, l0);mv.visitFieldInsn(Opcodes.GETSTATIC, 'java/lang/System', 'out', 'Ljava/io/PrintStream;');mv.visitLdcInsn('bytecode replaced!');mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, 'java/io/PrintStream', 'println', '(Ljava/lang/String;)V', false);Label l1 = new Label();mv.visitLabel(l1);mv.visitLineNumber(20, l1);mv.visitInsn(Opcodes.RETURN);mv.visitMaxs(2, 0);mv.visitEnd();TransformTarget.printSomething(); } return mv;} }}Attacher

使用 tools.jar 里方法將 agent 動態加載到目標 JVM 的類。

public class Attacher { public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException {VirtualMachine vm = VirtualMachine.attach('34242'); // 目標 JVM pidvm.loadAgent('/path/to/agent.jar'); }}

這樣,先啟動 TransformTarget 類,獲取到 pid 后將其傳入 Attacher 里,并指定 agent jar,將 agent attach 到 TransformTarget 中,原來輸出的 “hello” 就變成我們想要修改的 “bytecode replaced!” 了。

詳解Java動態字節碼技術

小結

掌握了字節碼的動態修改技術后,再回頭看 Btrace 的原理就更清晰了,稍微摸索一下我們也可以實現一個簡版的。另外很多大牛實現的各種 Java 性能分析工具的技術棧也不外如此,了解了這些,未來我們也可以寫出適合自己的工具,至少能對別人的工具進行修改~

不得不說 Java 的生態真的非常繁榮,當真是博大精深,查閱一個模塊的資料時能總引出一大堆新的概念,永遠有學不完的新東西。

以上就是詳解Java動態字節碼技術的詳細內容,更多關于Java動態字節碼技術的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
视频在线观看91| 影音先锋久久| 日韩精品一卡二卡三卡四卡无卡| 六月婷婷一区| 国产欧美啪啪| 成人av动漫在线观看| 日韩精品久久理论片| 国内不卡的一区二区三区中文字幕| 亚洲高清成人| 国产麻豆一区二区三区| 极品日韩av| 美女精品视频在线| 欧美专区在线| 日韩大片在线观看| 亚洲tv在线| 日韩一区电影| 日韩av在线播放中文字幕| 日本一区二区高清不卡| 国产一区二区中文| 国产精品一区亚洲| 欧美手机在线| 欧美午夜三级| 欧美日韩国产免费观看视频| 欧美一区影院| 亚洲女同一区| 四虎8848精品成人免费网站| 亚洲九九精品| 91精品国产乱码久久久久久久| 欧美日韩一区自拍| 亚洲国产专区校园欧美| 国产乱码精品一区二区三区亚洲人| 精品中文字幕一区二区三区av| 欧美日韩一区二区三区四区在线观看 | 欧美另类综合| 麻豆精品新av中文字幕| 免费成人性网站| 精品国产乱码久久久久久1区2匹| 老鸭窝毛片一区二区三区| a天堂资源在线| 国产精品传媒麻豆hd| 夜久久久久久| 色88888久久久久久影院| 欧美在线看片| 亚洲一区国产| 国内激情久久| 日本午夜大片a在线观看| 日韩一区二区三区四区五区| 亚洲在线网站| 国产国产精品| 成人福利av| 美女av一区| 日本亚洲三级在线| 国产在线不卡| 欧美日韩精品免费观看视欧美高清免费大片| 日韩av资源网| 一区二区电影在线观看| 欧美午夜不卡| 久久国产小视频| 麻豆久久久久久久| 日韩有吗在线观看| 蜜桃av一区二区| 亚洲一区区二区| 欧美成人高清| 久久久精品久久久久久96 | 婷婷综合六月| 国产精品xxx在线观看| 日韩成人一级| 日韩国产在线观看一区| 亚洲人www| 最新国产精品久久久| 9国产精品视频| 一本色道精品久久一区二区三区| 99视频精品视频高清免费| 综合日韩av| 999久久久免费精品国产| 国产精品久久久久久久免费观看| 美女精品久久| 高清不卡一区| 日韩1区在线| 久久久久久色| 7m精品国产导航在线| 日韩欧美中文在线观看| 日韩av中文字幕一区| 欧美精品国产一区| 欧美一区精品| 久久精品欧洲| 国产一区二区三区探花| 国产va免费精品观看精品视频| 精品深夜福利视频| 久久精品九色| av资源新版天堂在线| 久久久人人人| 欧美日韩国产综合网| 黄色日韩在线| 蜜臀久久99精品久久久久久9| 男人的天堂久久精品| 日韩欧美激情电影| 欧美激情视频一区二区三区在线播放| 麻豆一区二区三区| 色偷偷色偷偷色偷偷在线视频| 精品国模一区二区三区| 午夜精品亚洲| 日日夜夜免费精品| 国产精品久一| 黑森林国产精品av| 欧洲激情综合| 亚洲bt欧美bt精品777| 久久精品av麻豆的观看方式| 另类综合日韩欧美亚洲| 亚洲成人va| 免费不卡在线视频| 国产福利一区二区精品秒拍 | 夜久久久久久| 日韩一二三区在线观看| 久久av免费看| 亚洲不卡系列| 日本欧美在线看| 久久中文字幕一区二区| 亚洲不卡av不卡一区二区| 亚洲伊人精品酒店| 精品视频在线观看网站| 99久久精品费精品国产| 亚洲欧洲国产精品一区| 国产成人调教视频在线观看| 在线日韩电影| 日韩国产在线观看| 久久男人av资源站| 99国产一区| 国产精品久久久久久久久久久久久久久 | 久久国内精品视频| 国产成人在线中文字幕| 欧美日韩一二| 91亚洲无吗| 日韩在线观看一区| 日韩制服丝袜av| 国产a亚洲精品| 亚洲美洲欧洲综合国产一区| 国产日韩一区二区三区在线| 国产麻豆久久| 欧美一级二级视频| 999国产精品| 欧美日韩黄网站| 久久五月天小说| 国产精品亚洲欧美一级在线| 欧美日韩在线二区| 日韩激情中文字幕| 欧美精品一区二区久久| 国产麻豆一区二区三区精品视频| 日韩国产网站| 国产剧情在线观看一区| 欧美日韩国产高清| 欧美国产极品| 中文字幕免费精品| 欧洲av一区二区| 国产人成精品一区二区三| 一区视频在线| 精品久久电影| 日韩欧美另类中文字幕| 亚洲第一区色| 国产精品一级| 日本大胆欧美人术艺术动态| 久久久777| 久久69成人| 亚洲精品亚洲人成在线观看| 日韩高清成人| 国产精品超碰| 日韩欧美中文字幕电影| 激情综合自拍| 国产h片在线观看| 激情欧美一区二区三区| 日本天堂一区| 亚洲精品自拍| 亚洲久久视频| 亚洲精品无吗| 蜜桃一区二区三区在线| 香蕉精品999视频一区二区| 国产精品外国| 久久精品国产亚洲aⅴ| 日韩欧美精品| 国产成人精品一区二区三区视频| 国产日韩欧美一区二区三区 | 激情综合自拍| 日本黄色精品| 国产精品激情| 日韩国产欧美三级| 亚洲一区成人| 国产一区二区中文| 国产精品久久观看| 久久精品一本| 麻豆视频久久| 精品一区二区三区亚洲| 久久国产精品免费一区二区三区| 三级亚洲高清视频| 久久亚洲影院| 日韩网站在线| 亚洲精华国产欧美| 欧美在线亚洲| 99国产精品视频免费观看一公开| 99久久九九| 99久久久国产精品美女|