使用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 實現計時器,統計執行時長Python
- python效能優化之函式執行時間分析Python優化函式
- 1026 程式執行時間(四捨五入,round函式)函式
- 在JS中統計函式執行次數JS函式
- 痞子衡嵌入式:嵌入式裡通用微秒(microseconds)計時函式框架設計與實現ROS函式框架
- gohook 一個支援執行時替換 golang 函式的庫實現HookGolang函式
- 前端進階-執行時函式前端函式
- linux系統時間程式設計(6) 日曆時間tm轉字串strftime函式Linux程式設計字串函式
- PHP 時間函式PHP函式
- juc包:使用 juc 包下的顯式 Lock 實現執行緒間通訊執行緒
- Stopwatch 計算程式執行時間
- React 中 render 函式的執行時機React函式
- 執行時框架,編譯時框架框架編譯
- c++如何使用pthread_join函式配合pthread_create函式來建立和等待執行緒完成,實現執行緒同步與控制C++thread函式執行緒
- MySQL空間函式實現位置打卡MySql函式
- Oracle計算時間函式(對時間的加減numtodsinterval、numtoyminterval)Oracle函式
- T-SQL——函式——時間操作函式SQL函式
- 自執行函式函式
- 立即執行函式函式
- python程式計算執行時間差Python
- mysql執行函式出現1418錯誤MySql函式
- Clickhouse 時間日期函式函式
- 使用函式計算自定義執行時快速部署一個 SpringBoot 專案 | 文末有禮函式Spring Boot
- Linux QoS實現框架:函式呼叫流程圖Linux框架函式流程圖
- 實時計算框架:Flink叢集搭建與執行機制框架
- 區間統計 聚合函式組合器函式
- 使用Java實現多執行緒程式設計Java執行緒程式設計
- 時間函式:與時間相關那些事。。。函式
- MySQL 日期函式、時間函式在實際場景中的應用MySql函式
- 深入理解 函式、匿名函式、自執行匿名函式函式
- SPL 的日期時間函式函式
- FreeRTOS-04-核心控制函式+時間管理函式函式
- python函式每日一講 - exec執行函式Python函式
- 「python函式:」給定一個函式,怎麼在不修改原始碼的前提下,實現函式執行前和執行後輸出Python函式原始碼