摘要:什么是字節碼程序通過編譯之后生成文件就是字節碼集合正是有這樣一種中間碼字節碼,使得等函數語言只用實現一個編譯器即可運行在上。
什么是字節碼?
java程序通過javac編譯之后生成文件.class就是字節碼集合,正是有這樣一種中間碼(字節碼),使得scala/groovy/clojure等函數語言只用實現一個編譯器即可運行在JVM上。
看看一段簡單代碼。
public long getExclusiveTime() { long startTime = System.currentTimeMillis(); System.out.printf("exclusive code"); long endTime = System.currentTimeMillis(); return endTime - startTime; } public class com.blueware.agent.StartAgent {
編譯后通過命令(javap -c com.blueware.agent.StartAgent)查看,具體含義請參考oracle
public com.blueware.agent.StartAgent(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."為什么要學習字節碼?":()V 4: return public long getExclusiveTime(); Code: 0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 3: lstore_1 4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #4 // String exclusive code 9: iconst_0 10: anewarray #5 // class java/lang/Object 13: invokevirtual #6 // Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; 16: pop 17: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 20: lstore_3 21: lload_3 22: lload_1 23: lsub 24: lreturn }
能了解技術背后的原理,更容易寫出高質量代碼;
字節碼設計非常優秀,發展十幾年只僅僅刪除和增加幾個指令,學懂之后長期受益高,如果懂字節碼再學習scala/groovy/clojure會容易很多;
開發框架、監控系統、中間件、語言字節碼技術都是必殺技;
字節碼框架(ASM/Javassist)操作字節碼框架有很多,具體可以參考博文,下面對比ASM/Javassist
選項 | 優點 | 缺點 |
---|---|---|
ASM | 速度快、代碼量小、功能強大 | 要寫字節碼、學習曲線高 |
Javassist | 學習簡單,不用寫字節碼 | 比ASM慢,功能少 |
指的是可以用獨立于應用程序之外的代理(agent)程序,agent程序通過增強字節碼動態修改或者新增類,利用這樣特性可以設計出更通用的監控、框架、中間件程序,在JVM啟動參數加–javaagent:agent_jar_path/agent.jar即可運行(在JDK5及其后續版本才可以),更多關于Instrumentation知識請參考博文
計算方法執行時間方式直接在代碼開始和結束出打印當前時間,相減即可得到;
實現一個動態代理,或者借助Spring/AspectJ等框架;
上面兩種實現方式都需要修改代碼或者配置文件,下面我要介紹方式不僅不需要修改代碼,而且效率高;
具體實現方式1.StartAgent類必須提供premain方法,代碼如下:
public class StartAgent { //代理程序入口函數 public static void premain(String args, Instrumentation inst) { System.out.println("agent begin"); //添加字節碼轉換器 inst.addTransformer(new PrintTimeTransformer()); System.out.println("agent end"); } }
2.PrintTimeTransformer實現一個轉換器,代碼如下:
//字節碼轉化器類 public class PrintTimeTransformer implements ClassFileTransformer { //實現字節碼轉化接口,一個小技巧建議實現接口方法時寫@Override,方便重構 //loader:定義要轉換的類加載器,如果是引導加載器,則為 null(在這個小demo暫時還用不到) //className:完全限定類內部形式的類名稱和中定義的接口名稱,例如"java.lang.instrument.ClassFileTransformer" //classBeingRedefined:如果是被重定義或重轉換觸發,則為重定義或重轉換的類;如果是類加載,則為 null //protectionDomain:要定義或重定義的類的保護域 //classfileBuffer:類文件格式的輸入字節緩沖區(不得修改) //一個格式良好的類文件緩沖區(轉換的結果),如果未執行轉換,則返回 null。 @Override public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //簡化測試demo,直接寫待修改的類(com/blueware/agent/TestTime) if (className != null && className.equals("com/blueware/agent/TestTime")) { //讀取類的字節碼流 ClassReader reader = new ClassReader(classfileBuffer); //創建操作字節流值對象,ClassWriter.COMPUTE_MAXS:表示自動計算棧大小 ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS); //接受一個ClassVisitor子類進行字節碼修改 reader.accept(new TimeClassVisitor(writer, className), 8); //返回修改后的字節碼流 return writer.toByteArray(); } return null; } }
3.TimeClassVisitor類訪問器,實現字節碼修改,代碼如下:
//定義掃描待修改class的visitor,visitor就是訪問者模式 public class TimeClassVisitor extends ClassVisitor { private String className; public TimeClassVisitor(ClassVisitor cv, String className) { super(Opcodes.ASM5, cv); this.className = className; } //掃描到每個方法都會進入,參數詳情下一篇博文詳細分析 @Override public MethodVisitor visitMethod(int access, final String name, final String desc, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); final String key = className + name + desc; //過來待修改類的構造函數 if (!name.equals("") && mv != null) { mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) { //方法進入時獲取開始時間 @Override public void onMethodEnter() { //相當于com.blueware.agent.TimeUtil.setStartTime("key"); this.visitLdcInsn(key); this.visitMethodInsn(Opcodes.INVOKESTATIC, "com/blueware/agent/TimeUtil", "setStartTime", "(Ljava/lang/String;)V", false); } //方法退出時獲取結束時間并計算執行時間 @Override public void onMethodExit(int opcode) { //相當于com.blueware.agent.TimeUtil.setEndTime("key"); this.visitLdcInsn(key); this.visitMethodInsn(Opcodes.INVOKESTATIC, "com/blueware/agent/TimeUtil", "setEndTime", "(Ljava/lang/String;)V", false); //向棧中壓入類名稱 this.visitLdcInsn(className); //向棧中壓入方法名 this.visitLdcInsn(name); //向棧中壓入方法描述 this.visitLdcInsn(desc); //相當于com.blueware.agent.TimeUtil.getExclusiveTime("com/blueware/agent/TestTime","testTime"); this.visitMethodInsn(Opcodes.INVOKESTATIC, "com/blueware/agent/TimeUtil", "getExclusiveTime", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J", false); } }; } return mv; } }
4.TimeClassVisitor記錄時間幫助類,代碼如下:
public class TimeUtil { private static Map題記startTimes = new HashMap (); private static Map endTimes = new HashMap (); private TimeUtil() { } public static long getStartTime(String key) { return startTimes.get(key); } public static void setStartTime(String key) { startTimes.put(key, System.currentTimeMillis()); } public static long getEndTime(String key) { return endTimes.get(key); } public static void setEndTime(String key) { endTimes.put(key, System.currentTimeMillis()); } public static long getExclusiveTime(String className, String methodName, String methodDesc) { String key = className + methodName + methodDesc; long exclusive = getEndTime(key) - getStartTime(key); System.out.println(className.replace("/", ".") + "." + methodName + " exclusive:" + exclusive); return exclusive; } }
上面的代碼難免有bug,如果你發現代碼寫的有問題,請你幫忙指出,讓我們一起進步,讓代碼變的更漂亮和健壯;
順便打點廣告,如果看后對字節碼技術感興趣,歡迎加入我們oneapm,一起做點有意思事情,可直接聯系我;
完整代碼請訪問github;
下一篇結合demo再深入研究ClassVisitor
OneAPM 為您提供端到端的 Java 應用性能解決方案,我們支持所有常見的 Java 框架及應用服務器,助您快速發現系統瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監控從來沒有如此簡單。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客,還可以掃碼關注下方的Java程序性能優化公眾號。
本文轉自 OneAPM 官方博客
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65531.html
摘要:基于局部性原理,計算機處理器在設計時做了各種優化,比如現代的多級分支預測有良好局部性的程序比局部性差的程序運行得更快。目前計算機設計中,都是以塊頁為單位管理調度存儲,其實就是在利用空間局部性來優化性能。 學過計算機底層原理、了解過很多架構設計或者是做過優化的同學,應該很熟悉局部性原理。即便是非計算機行業的人,在做各種調優、提效時也不得不考慮到局部性,只不過他們不常用局部性一詞。如果...
摘要:我下圖代碼第五行和第九行分別定義了一個整型變量和一個整型常量程序員都知道兩者的區別。下面我們就用將文件反編譯出來然后深入研究里整型變量和整型常量的區別。 我下圖代碼第五行和第九行分別定義了一個整型變量和一個整型常量: static final int number1 = 512; static int number3 = 545; Java程序員都知道兩者的區別。 showImg(ht...
摘要:寫在前面博客主頁的江湖背景的江湖背景歡迎關注點贊收藏留言本文由原創,首發首發時間年月日最新更新時間年月日堅持和努力一定能換來詩與遠方向未見花聞學習參考書籍深入理解計算機系統作者水平很有限,如果發現錯誤,請留言轟炸哦萬分感謝感謝感謝 ?寫在前面 ?博客主頁:kikoking的江湖背景?...
摘要:由虛擬機加載的類,被加載到虛擬機內存中之后,虛擬機會讀取并執行它里面存在的字節碼指令。虛擬機中執行字節碼指令的部分叫做執行引擎。 什么是Java虛擬機? 作為一個Java程序員,我們每天都在寫Java代碼,我們寫的代碼都是在一個叫做Java虛擬機的東西上執行的。但是如果要問什么是虛擬機,恐怕很多人就會模棱兩可了。在本文中,我會寫下我對虛擬機的理解。因為能力所限,可能有些地方描述的不夠欠...
閱讀 3652·2021-09-02 15:11
閱讀 4563·2021-08-16 10:47
閱讀 1560·2019-08-29 18:35
閱讀 3030·2019-08-28 17:54
閱讀 2843·2019-08-26 11:37
閱讀 1496·2019-08-23 16:51
閱讀 1799·2019-08-23 14:36
閱讀 1801·2019-08-23 14:21