如何在智能告警平台CA触发测试告警
688
2022-09-25
深入字节码 -- 计算方法执行时间(字节 深圳)
什么是字节码?
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 {
public com.blueware.agent.StartAgent(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."
为什么要学习字节码?
能了解技术背后的原理,更容易写出高质量代码;字节码设计非常优秀,发展十几年只仅仅删除和增加几个指令,学懂之后长期受益高,如果懂字节码再学习scala/groovy/clojure会容易很多;开发框架、监控系统、中间件、语言字节码技术都是必杀技;
字节码框架(ASM/Javassist)
|选项 | 优点 |缺点 ||--------------|----------|-------------||ASM |速度快、代码量小、功能强大|要写字节码、学习曲线高||Javassist|学习简单,不用写字节码|比ASM慢,功能少|
Java 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("
4.TimeClassVisitor记录时间帮助类,代码如下:
public class TimeUtil { private static Map
题记
发表评论
暂时没有评论,来抢沙发吧~