使用ASM框架實現統計函式執行時間
定義
ASM 是一個 Java 位元組碼操控框架。它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進位制 class 檔案,也可以在類被載入入 Java 虛擬機器之前動態改變類行為。Java class 被儲存在嚴格格式定義的 .class 檔案裡,這些類檔案擁有足夠的後設資料來解析類中的所有元素:類名稱、方法、屬性以及 Java 位元組碼(指令)。ASM 從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者要求生成新類。
實現
現在使用ASM框架,在不修改原始碼的情況下實現統計函式執行時間
定義一個方法,在方法裡使用Thread.sleep() 模擬函式呼叫的耗時
public class Test {
public void m() {
System.out.println("方法執行了...");
try {
Thread.sleep((long) ((Math.random() + 1) * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
需要加入的統計功能,TimeStat類定義了兩個方法,start()方法表示函式呼叫開始,end()方法表示函式呼叫結束,並記錄函式的執行耗時
public class TimeStat {
static ThreadLocal<Long> t = new ThreadLocal<Long>();
public static void start() {
t.set(System.currentTimeMillis());
}
public static void end() {
System.out.println(Thread.currentThread().getStackTrace()[2] + " spend:" + (System.currentTimeMillis() - t.get()));
}
}
將TimeStat的start、end方法織入Test.m()
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class TimeStatWeaveGenerator {
public static void main(String[] args) throws IOException {
String className = Test.class.getName();
ClassReader cr = new ClassReader(className);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
TimeStatClassAdapter classAdapter = new TimeStatClassAdapter(cw);
cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
byte[] bytes = cw.toByteArray();
//file地址為本地的class檔案路徑
File file = new File("G:\\project\\JVM\\target\\classes/" + className.replaceAll("\\.", "/") + ".class");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bytes);
fileOutputStream.close();
}
}
TimeStatClassAdapter類完成具體的位元組碼修改工作,重寫ClassVisitor類,覆蓋visitMethod方法,對m()方法進行修改,判斷是否是m()方法,如果是則進行方法位元組碼的調整,並將這個工作委託給TimeStatMethodAdapter完成
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
public class TimeStatClassAdapter extends ClassVisitor {
public TimeStatClassAdapter(ClassVisitor classVisitor) {
super(Opcodes.ASM5, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
MethodVisitor adapter = methodVisitor;
if (methodVisitor != null) {
if (name.equals("m")) {
adapter = new TimeStatMethodAdapter(methodVisitor);
}
}
return adapter;
}
}
TimeStatClassAdapter visitCode()在方法的Code屬性被訪問時呼叫,在此時插入TimeStat.start()方法,表示方法的開始,覆蓋visitInsn()方法,當訪問xreturn指令 時,呼叫TimeStat.end()方法
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
public class TimeStatMethodAdapter extends MethodVisitor {
public TimeStatMethodAdapter(MethodVisitor methodVisitor) {
super(Opcodes.ASM5, methodVisitor);
}
@Override
public void visitCode() {
//第二個引數為TimeStat類的全限定名
visitMethodInsn(Opcodes.INVOKESTATIC, "xx/xx/xx/TimeStat", "start", "()V");
super.visitCode();
}
@Override
public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN)) {
visitMethodInsn(Opcodes.INVOKESTATIC, "xx/xx/xx/TimeStat", "end", "()V");
}
mv.visitInsn(opcode);
}
}
執行方法
public class RunASMMain {
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.m();
}
}
先執行TimeStatWeaveGenerator再執行RunASMMain,結果如下:
方法執行了...
xx.xx.xx.Test.m(Unknown Source) spend:1955
相關文章
- Golang時間函式及測試函式執行時間案例Golang函式
- linux系統時間程式設計(9) 計算程式片段執行時間clock函式Linux程式設計函式
- python之為函式執行設定超時時間(允許函式執行的最大時間)Python函式
- GO語言————6.11 計算函式執行時間Go函式
- python用time函式計算程式執行時間Python函式
- js如何實現一定時間後去執行一個函式JS函式
- VC程式執行時間測試函式C程式函式
- python 實現計時器,統計執行時長Python
- Node.js使用計時器定時執行函式詳解Node.js函式
- 請求執行時間段與Shell函式(轉)函式
- 使用 ES6 來為非同步函式記錄執行時間非同步函式
- python效能優化之函式執行時間分析Python優化函式
- Swift實現多執行緒map函式Swift執行緒函式
- C# 程式找出檔案重複的行,計算函式執行的時間C#函式
- gohook 一個支援執行時替換 golang 函式的庫實現HookGolang函式
- 在JS中統計函式執行次數JS函式
- 前端進階-執行時函式前端函式
- 常用函式--時間函式函式
- 時間函式函式
- 計算SQL執行時間SQL
- javascript 事件觸發以後函式指定時間後再執行JavaScript事件函式
- 痞子衡嵌入式:嵌入式裡通用微秒(microseconds)計時函式框架設計與實現ROS函式框架
- C#動態執行函式:利用反射實現C#函式反射
- 實戰VC時間控制函式 (轉)函式
- python獲取系統時間(時間函式詳解)Python函式
- 執行時框架,編譯時框架框架編譯
- PHP 時間函式PHP函式
- 日期時間函式函式
- oracle時間函式Oracle函式
- MySQL時間函式MySql函式
- Sql時間函式SQL函式
- 函數語言程式設計 - 實現響應式框架函數程式設計框架
- 1026 程式執行時間(四捨五入,round函式)函式
- C# 中利用執行時編譯實現泛函C#編譯
- juc包:使用 juc 包下的顯式 Lock 實現執行緒間通訊執行緒
- React 中 render 函式的執行時機React函式
- sql 獲取系統時間的函式。SQL函式
- Oracle計算時間函式(對時間的加減numtodsinterval、numtoyminterval)Oracle函式