- 浏览: 426145 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
Glogo:
楼主您好,我在试验您的代码的时候发现新开的三个子线程并没有一直 ...
java 高并发 ReentrantLock -- 可重入的锁 -
univasity:
最近发觉也被限速了,投诉一下就好一会~~ 看来明天又要和电信M ...
ADSL上网速度慢 都是帐号限速惹的祸 -
liuyuanhui0301:
java 高并发 ReentrantLock -- 可重入的锁 -
dang_java:
呵.很好的说明文档.
JXTA技术与应用发展 -
helloqidi:
谢谢,学习了
SQL中exists和in的区别
在前面的篇章中,我们看到Java Instrutment的强大能力,本篇,我们将介绍如何使用ObjectWeb ASM的字节码增强能力构建Method Monitor
1.什么是ObjectWeb ASM
在我看来,ObjectWeb ASM具有如下几个非常诱人的特点
- 小巧、高效
- 源代码实现非常简洁而又优雅,简直就是Gof的《设计模式》非常棒的注解
- 字节码级的控制,能够更高效地实现字节码的控制
ObjectWeb ASM有2组接口:
- 基于事件驱动的接口,类似于xml的SAX接口,visitor模式,在访问到类定义某个部分的时候进行回调,实现上比tree接口高效,占用内存更小
- 基于tree的接口,类似于xml的DOM接口,将类定义解析成tree
这里我们将使用ObjectWeb ASM的事件驱动接口
2. 目标
我们将对已有的字节码进行增强,收集进入方法和退出方法的信息,这里主要解决Method Monitor的字节码增强部分,不对收集后的数据处理做更深入地研究,出于演示的目的,我们定义了如下的收集方法的访问信息处理,在实际应用中,我们可能会使用更好的格式收集更多的数据、使用异步处理提高性能、使用批量处理提高处理能力、使用友好的UI显示信息等等,此处不对这部分进行探讨
- package blackstar.methodmonitor.instrutment.monitor;
- public class MonitorUtil
- {
- public final static String CLASS_NAME = MonitorUtil.class.getName()
- .replaceAll("\\.", "/");
- public final static String ENTRY_METHOD = "entryMethod";
- public final static String EXIT_METHOD = "exitMethod";
- public final static String METHOD = "(Ljava/lang/String;Ljava/lang/String;)V";
- public static void entryMethod(String className, String methodName)
- {
- System.out.println("entry : " + className + "." + methodName);
- }
- public static void exitMethod(String className, String methodName)
- {
- System.out.println("exit : " + className + "." + methodName);
- }
- }
package blackstar.methodmonitor.instrutment.monitor; public class MonitorUtil { public final static String CLASS_NAME = MonitorUtil.class.getName() .replaceAll("\\.", "/"); public final static String ENTRY_METHOD = "entryMethod"; public final static String EXIT_METHOD = "exitMethod"; public final static String METHOD = "(Ljava/lang/String;Ljava/lang/String;)V"; public static void entryMethod(String className, String methodName) { System.out.println("entry : " + className + "." + methodName); } public static void exitMethod(String className, String methodName) { System.out.println("exit : " + className + "." + methodName); } }
3. 从字节码开始
实际上,对于被监控制的代码,我们所需要实现的功能如下,红色部分的代码是我们需要在动态期插到字节码中间的
{
try
{
methodEntry(…)
methodCode
}
finally
{
methodExit(…)
}
}
这个问题看起来简单,实际则没有那么容易,因为在JVM的字节码设计中,字节码并不直接支持finally语句,而是使用try…catch来模拟的,我们先来看一个例子
- package blackstar.methodmonitor.instrutment.test;
- public class Test
- {
- public void sayHello() throws Exception
- {
- try
- {
- System.out.println("hi");
- } catch (Exception e)
- {
- System.out.println("exception");
- return;
- } finally
- {
- System.out.println("finally");
- }
- }
- }
package blackstar.methodmonitor.instrutment.test; public class Test { public void sayHello() throws Exception { try { System.out.println("hi"); } catch (Exception e) { System.out.println("exception"); return; } finally { System.out.println("finally"); } } }
我们看看字节码是如何处理finally语句的
首先看看异常表,异常是在JVM级别上直接支持的,下面异常表的意思是,在执行0-8语句的时候,如果有异常java.lang.Exception抛出,则进入第11语句,在执行0-20语句的时候,有任何异常抛出,都进入29语句。实际上JVM是这样实现finally语句的:
- 在任何return语句之前,都会增加finally语句中的字节码
- 定义一个捕获所有异常的语句,增加finally语句中的字节码,如果finally中没有return语句,则会将异常再次抛出去(处理方法以抛出异常的方式结束)
[0-8): 11 - java.lang.Exception
[0-20): 29
我们再看看字节码具体是如何做的
3 ldc "hi" (java.lang.String)
5 invokevirtual println
8 goto 40
// System.out.println("hi");,执行完之后执行返回(goto 40)
11 astore_1
12 getstatic java.lang.System.out
15 ldc "exception" (java.lang.String)
17 invokevirtual println
// System.out.println("exception");
20 getstatic java.lang.System.out
23 ldc "finally" (java.lang.String)
25 invokevirtual println
// return语句之前插入finally部分字节码
// System.out.println("finally");
28 return
29 astore_2
30 getstatic java.lang.System.out
33 ldc "finally" (java.lang.String)
35 invokevirtual println
38 aload_2
39 athrow
//当在执行0-29语句中,如果有异常抛出,则执行这段finally语句
//此处的astore_2(将栈顶值——即exception的地址——设给第2个local变量)和aload_2(将第2个local变量的值入栈)这两个字节码实际是不必要的,但需要注意的是,如果这2段代码去掉的话,要考虑增大操作栈(max stack)以容纳这个exception地址
//System.out.println("finally");
40 getstatic java.lang.System.out
43 ldc "finally" (java.lang.String)
45 invokevirtual println
// return语句之前插入finally部分字节码
// System.out.println("finally");
48 return
实际上,我们需要做的就是
- 在方法进入时插入方法进入代码(需要注意,对于构造函数不允许做这种处理,构造函数第一步必须调用父类的构造函数。
- 在每个return操作(包括return、ireturn、freturn等)之前,插入方法退出代码
- 定义一个捕获所有异常的处理,在处理中,插入方法退出代码(即方法以抛异常的方式终止执行)
4. 实现
我们看看使用ObjectWeb ASM如何实现我们上面描述的功能
1)ObjectWeb ASM的字节码修改
- ClassReader cr = new ClassReader(byteArray); //使用字节码构监一个reader
- ClassWriter cw = new ClassWriter(cr, 0);//writer将基于已有的字节码进行修改
- MonitorClassVisitor ca = new MonitorClassVisitor(cw);//修改处理回调类
- cr.accept(ca, 0);
ClassReader cr = new ClassReader(byteArray); //使用字节码构监一个reader ClassWriter cw = new ClassWriter(cr, 0);//writer将基于已有的字节码进行修改 MonitorClassVisitor ca = new MonitorClassVisitor(cw);//修改处理回调类 cr.accept(ca, 0);
2)定制MonitorClassVisitor,主要处理逻辑在MonitorAdapter部分
- package blackstar.methodmonitor.instrutment;
- import org.objectweb.asm.ClassAdapter;
- import org.objectweb.asm.ClassVisitor;
- import org.objectweb.asm.MethodVisitor;
- /**
- * @author raywu (ayufox@gmail.com)
- *
- */
- public class MonitorClassVisitor extends ClassAdapter
- {
- private String className;
- public MonitorClassVisitor(ClassVisitor classvisitor)
- {
- super(classvisitor);
- }
- public void visit(int version, int access, String name, String signature,
- String superName, String[] interfaces)
- {
- this.className = name.replaceAll("/", ".");
- super.visit(version, access, name, signature, superName, interfaces);
- }
- public MethodVisitor visitMethod(int access, String name, String desc,
- String signature, String[] exceptions)
- {
- MethodVisitor visitor = super.visitMethod(access, name, desc,
- signature, exceptions);
- //构造函数不修改字节码
- if ("<init>".equals(name))
- {
- return visitor;
- }
- //类定义初始化方法不修改字节码
- if ("<cinit>".equals(name))
- {
- return visitor;
- }
- //main函数不修改字节码
- if ("main".equals(name))
- {
- return visitor;
- }
- return new MonitorAdapter(className, name, visitor);
- }
- }
package blackstar.methodmonitor.instrutment; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; /** * @author raywu (ayufox@gmail.com) * */ public class MonitorClassVisitor extends ClassAdapter { private String className; public MonitorClassVisitor(ClassVisitor classvisitor) { super(classvisitor); } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.className = name.replaceAll("/", "."); super.visit(version, access, name, signature, superName, interfaces); } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions); //构造函数不修改字节码 if ("<init>".equals(name)) { return visitor; } //类定义初始化方法不修改字节码 if ("<cinit>".equals(name)) { return visitor; } //main函数不修改字节码 if ("main".equals(name)) { return visitor; } return new MonitorAdapter(className, name, visitor); } }
3) 定制MethodVisitor
- package blackstar.methodmonitor.instrutment;
- import org.objectweb.asm.Label;
- import org.objectweb.asm.MethodAdapter;
- import org.objectweb.asm.MethodVisitor;
- import org.objectweb.asm.Opcodes;
- import blackstar.methodmonitor.instrutment.monitor.MonitorUtil;
- /**
- * @author raywu (ayufox@gmail.com)
- *
- */
- public class MonitorAdapter extends MethodAdapter implements Opcodes
- {
- private final static int MONITOR_STACK = 2 + 1;//max_stack至少需要能够容纳2个常量地址(监控方法使用)和1个exception地址
- private String className;
- private String methodName;
- private Label start = new Label();// 方法方法字节码开始位置
- private Label end = new Label();// 方法方法字节码结束位置
- public MonitorAdapter(String className, String methodName, MethodVisitor mv)
- {
- super(mv);
- this.className = className;
- this.methodName = methodName;
- }
- public void visitCode()
- {
- mv.visitCode();
- mv.visitLabel(start);// 设置开始标志
- //在方法开始位置,增加entry监控
- mv.visitLdcInsn(this.className);
- mv.visitLdcInsn(this.methodName);
- mv.visitMethodInsn(INVOKESTATIC, MonitorUtil.CLASS_NAME,
- MonitorUtil.ENTRY_METHOD, MonitorUtil.METHOD);
- }
- public void visitInsn(int opcode)
- {
- //在所有return子句之前,增加exit监控
- if (opcode >= IRETURN && opcode <= RETURN)
- {
- mv.visitLdcInsn(this.className);
- mv.visitLdcInsn(this.methodName);
- mv.visitMethodInsn(INVOKESTATIC, MonitorUtil.CLASS_NAME,
- MonitorUtil.EXIT_METHOD, MonitorUtil.METHOD);
- }
- mv.visitInsn(opcode);
- }
- public void visitEnd()
- {
- //从方法开始位置start到方法结束位置end部分,
- //处理方法使用抛出异常的方式终结方法执行
- mv.visitLabel(end);
- mv.visitTryCatchBlock(start, end, end, null);
- mv.visitLdcInsn(this.className);
- mv.visitLdcInsn(this.methodName);
- mv.visitMethodInsn(INVOKESTATIC, MonitorUtil.CLASS_NAME,
- MonitorUtil.EXIT_METHOD, MonitorUtil.METHOD);
- mv.visitInsn(ATHROW); // 重新把异常抛出
- mv.visitEnd();
- }
- public void visitMaxs(int maxStack, int maxLocals)
- {
- //保证max stack足够大
- super.visitMaxs(Math.max(MONITOR_STACK, maxStack), maxLocals);
- }
- }
package blackstar.methodmonitor.instrutment; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import blackstar.methodmonitor.instrutment.monitor.MonitorUtil; /** * @author raywu (ayufox@gmail.com) * */ public class MonitorAdapter extends MethodAdapter implements Opcodes { private final static int MONITOR_STACK = 2 + 1;//max_stack至少需要能够容纳2个常量地址(监控方法使用)和1个exception地址 private String className; private String methodName; private Label start = new Label();// 方法方法字节码开始位置 private Label end = new Label();// 方法方法字节码结束位置 public MonitorAdapter(String className, String methodName, MethodVisitor mv) { super(mv); this.className = className; this.methodName = methodName; } public void visitCode() { mv.visitCode(); mv.visitLabel(start);// 设置开始标志 //在方法开始位置,增加entry监控 mv.visitLdcInsn(this.className); mv.visitLdcInsn(this.methodName); mv.visitMethodInsn(INVOKESTATIC, MonitorUtil.CLASS_NAME, MonitorUtil.ENTRY_METHOD, MonitorUtil.METHOD); } public void visitInsn(int opcode) { //在所有return子句之前,增加exit监控 if (opcode >= IRETURN && opcode <= RETURN) { mv.visitLdcInsn(this.className); mv.visitLdcInsn(this.methodName); mv.visitMethodInsn(INVOKESTATIC, MonitorUtil.CLASS_NAME, MonitorUtil.EXIT_METHOD, MonitorUtil.METHOD); } mv.visitInsn(opcode); } public void visitEnd() { //从方法开始位置start到方法结束位置end部分, //处理方法使用抛出异常的方式终结方法执行 mv.visitLabel(end); mv.visitTryCatchBlock(start, end, end, null); mv.visitLdcInsn(this.className); mv.visitLdcInsn(this.methodName); mv.visitMethodInsn(INVOKESTATIC, MonitorUtil.CLASS_NAME, MonitorUtil.EXIT_METHOD, MonitorUtil.METHOD); mv.visitInsn(ATHROW); // 重新把异常抛出 mv.visitEnd(); } public void visitMaxs(int maxStack, int maxLocals) { //保证max stack足够大 super.visitMaxs(Math.max(MONITOR_STACK, maxStack), maxLocals); } }
4)我们看看最终会产生什么样的字节码,如下图,我们正确地在进入方法时加entry方法调用、每个return子句中插入exit方法调用、在方法异常抛出时插入exit方法调用
发表评论
-
深入JVM锁机制
2011-09-19 01:00 969目前在Java中存在两种 ... -
java 正则表达式 非捕获组(特殊构造)Special Constructs(Non-Capturing)
2011-06-20 23:15 1638针对Java API文档中的正则表达式关于特殊构造(非捕获组) ... -
Java文件映射[mmap]揭秘
2011-06-08 20:10 1162前言 相信现在 ... -
原创 java的Mmap二三事
2011-06-08 19:46 1192转自 :http://blog.csdn.net/kabini ... -
java 安全沙箱模型详解
2011-04-18 16:29 933起到第一道安全保障作 ... -
非阻塞算法-ReentrantLock代码剖析之ReentrantLock.lock
2011-04-15 13:59 1076ReentrantLock是java.util.concurr ... -
CyclicBarrier与CountDownLatch、栅栏与计数器
2011-04-15 10:39 1457在多线程设计中,我猜常常会遇到线程间相互等待以及某个线程等待1 ... -
Java KeyStore
2011-04-13 17:17 1460简介Java自带的keytool工具是个密钥和证书管理工具。它 ... -
Security Managers and the JavaTM 2 SDK
2011-04-12 13:37 766The original Link : http://do ... -
Something about SecurityManager
2011-04-12 13:33 776The Java Security was made up o ... -
Using the Java SecurityManager with Tomcat
2011-04-12 13:30 990Why use a SecurityManager? The ... -
Java安全管理器(Security Manager)(
2011-04-11 14:54 878转载自: http://blog.sina.com.cn/s/ ... -
Java对象的强、软、弱和虚引用(1)
2011-04-01 08:44 793本文介绍Java对象的强 ... -
Java对象的强引用、软引用、弱引用和虚引用
2011-04-01 08:39 900在JDK1.2以前的版本中, ... -
java 高并发 ReentrantLock -- 可重入的锁
2011-03-30 08:09 2353ReentrantLock -- 可重入的锁 可重入锁指 ... -
线程运行栈信息的获取
2011-03-24 17:23 1301线程运行栈信息的获取 ... -
java序列化——Serializable
2011-03-15 23:17 1064类通过实现 java.io.Serializable 接口 ... -
Java aio(异步网络IO)初探
2011-03-11 16:34 1554按照《Unix网络编程》的 ... -
JAVA NIO 简介
2011-03-11 13:38 11101. 基本 概念 IO 是主存和外部设备 ( 硬盘、终 ... -
深入了解Java ClassLoader、Bytecode 、ASM、cglib
2011-03-08 16:35 852一、Java ClassLoader 1,什 ...
相关推荐
asm字节码jar包,仅需要一个就可以。具体使用,可以参考http://blog.csdn.net/gaoshan12345678910/article/details/78131784
严重: Exception sending context initialized event to listener instance ... nested exception is java.lang.NoClassDefFoundError: org/objectweb/asm/Type ssh 框架的java web项目部署到tomcat报上面错误 添加此包
jar包,官方版本,自测可用
jar包,官方版本,自测可用
4. **字节码操作库**:为了修改字节码,我们需要引入一个库,如ASM、ByteBuddy或CGlib。这些库提供了API,让我们可以动态地创建、修改和分析字节码。 在"Gradle Plugin 修改其class 字节码的Demo"中,GroovyDemo的...
字节码是Java虚拟机(JVM)理解和执行的二进制指令,ASM通过提供一系列API,让开发者能够直接操作这些字节码,从而实现对Java类的深度定制。ASM的使用场景广泛,包括但不限于AOP(面向切面编程)、代码生成、动态...
jar包,官方版本,自测可用
asm是assembly的缩写,是汇编的称号,对于java而言,asm就是字节码级别的编程。 而这里说到的asm是指objectweb asm,一种.class的代码生成器的开源项目. ASM是一套java字节码生成架构,它可以动态生成二进制格式的...
在Java开发中,字节码框架ASM提供了一种强大的工具,允许程序员在运行时动态生成或修改类。ASM是一个底层的库,它直接操作Java字节码,这使得开发者能够在运行时改变类的行为或创建新的类。本文将深入探讨ASM框架的...
JMod是针对Java平台的一种模块化工具,它与ObjectWeb ASM库有关,ASM是一个流行的Java字节码操作和分析框架。ASM库广泛用于动态代理、代码生成和转换,以及一些性能优化工具。然而,尽管ASM功能强大,但在某些特定...
关于Cannot proxy target class because CGLIB2 is not...和 java.lang.ClassNotFoundException: org.objectweb.asm.Type错误的解决方法: 就是加入cglib-nodep-2.2.2.jar包,如果加入cglib-2.2.2.jar包,就会报第二种错误
离线安装包,亲测可用
java运行依赖jar包
jar包,官方版本,自测可用
`org.objectweb.asm` 是一个用于动态生成和分析Java字节码的库,通常在使用动态代理、AOP(面向切面编程)或自定义字节码操作时会用到。 `Type` 类是ASM库的一个关键组成部分,它提供了对Java类型表示的支持,包括...
jar包,官方版本,自测可用
相反,它是一个经过修改的 Minecraft 加载器,它使用 ObjectWeb ASM 字节码操作框架在运行时修改其类(为标识的字段注入“get”和“set”方法),允许使用易于使用的 API 构建自定义修改对于最终用户。 论坛主题
Oracle linux包