1、javap
class文件反汇编工具
public class Calc { public int calc() { int a = 500; int b = 200; int c = 50; return (a + b) / c; } } javap –verbose Calc 显示如下:
public int calc(); Code: Stack=2, Locals=4, Args_size=1 0: sipush 500 3: istore_1 4: sipush 200 7: istore_2 8: bipush 50 10: istore_3 11: iload_1 12: iload_2 13: iadd 14: iload_3 15: idiv 16: ireturn }
2、简单的字节码执行过程
3、常用的字节码
常量入栈 aconst_null null对象入栈 iconst_m1 int常量-1入栈 iconst_0 int常量0入栈 iconst_5 lconst_1 long常量1入栈 fconst_1 float 1.0入栈 dconst_1 double 1.0 入栈 bipush 8位带符号整数入栈 sipush 16位带符号整数入栈 ldc 常量池中的项入栈 局部变量压栈 xload(x为i l f d a) 分别表示int,long,float,double,object ref xload_n(n为0 1 2 3) xaload(x为i l f d a b c s) 分别表示int, long, float, double, obj ref ,byte,char,short 从数组中取得给定索引的值,将该值压栈 iaload 执行前,栈:..., arrayref, index 它取得arrayref所在数组的index的值,并将值压栈 执行后,栈:..., value 出栈装载入局部变量 xstore(x为i l f d a) 出栈,存入局部变量 xstore_n(n 0 1 2 3) 出栈,将值存入第n个局部变量 xastore(x为i l f d a b c s) 将值存入数组中 iastore 执行前,栈:...,arrayref, index, value 执行后,栈:... 将value存入arrayref[index] 通用栈操作(无类型) nop pop 弹出栈顶1个字长 dup 复制栈顶1个字长,复制内容压入栈 类型转化 i2l i2f l2i l2f l2d f2i f2d d2i d2l d2f i2b i2c i2s 例如: 将int转为long 执行前,栈:..., value 执行后,栈:...,result.word1,result.word2 弹出int,扩展为long,并入栈 整数运算 iadd ladd isub lsub idiv ldiv imul lmul iinc 浮点运算 fadd dadd fsub dsub fdiv ddiv fmul dmul 对象操作指令 new getfield putfield getstatic putstatic 条件控制 ifeq 如果为0,则跳转 ifne 如果不为0,则跳转 iflt 如果小于0 ,则跳转 ifge 如果大于0,则跳转 if_icmpeq 如果两个int相同,则跳转 例如: ifeq 参数 byte1,byte2 value出栈 ,如果栈顶value为0则跳转到(byte1<<8)|byte2 执行前,栈:...,value 执行后,栈:... 方法调用 invokevirtual invokespecial invokestatic invokeinterface xreturn(x为 i l f d a 或为空)
4、使用ASM生成Java字节码
ASM Java字节码操作框架 可以用于修改现有类或者动态产生新类 用户 AspectJ Clojure Ecplise spring cglib hibernate
4.1 示例1
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES); cw.visit(V1_7, ACC_PUBLIC, "Example", null, "java/lang/Object", null); MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mw.visitVarInsn(ALOAD, 0); //this 入栈 mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mw.visitInsn(RETURN); mw.visitMaxs(0, 0); mw.visitEnd(); mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mw.visitLdcInsn("Hello world!"); mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mw.visitInsn(RETURN); mw.visitMaxs(0,0); mw.visitEnd(); byte[] code = cw.toByteArray(); AsmHelloWorld loader = new AsmHelloWorld(); Class exampleClass = loader .defineClass("Example", code, 0, code.length); exampleClass.getMethods()[0].invoke(null, new Object[] { null });
4.2 示例2
模拟实现AOP字节码织入 - 在函数开始部分或者结束部分嵌入字节码 - 可用于进行鉴权、日志等
class AddSecurityCheckClassAdapter extends ClassVisitor { public AddSecurityCheckClassAdapter( ClassVisitor cv) { super(Opcodes.ASM5, cv); } // 重写 visitMethod,访问到 "operation" 方法时, // 给出自定义 MethodVisitor,实际改写方法内容 public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions); MethodVisitor wrappedMv = mv; if (mv != null) { // 对于 "operation" 方法 if (name.equals("operation")) { // 使用自定义 MethodVisitor,实际改写方法内容 wrappedMv = new AddSecurityCheckMethodAdapter(mv); } } return wrappedMv; } } class AddSecurityCheckMethodAdapter extends MethodVisitor { public AddSecurityCheckMethodAdapter(MethodVisitor mv) { super(Opcodes.ASM5,mv); } public void visitCode() { visitMethodInsn(Opcodes.INVOKESTATIC, "geym/jvm/ch10/asm/SecurityChecker", "checkSecurity", "()Z"); super.visitCode(); } } public class Generator{ public static void main(String args[]) throws Exception { ClassReader cr = new ClassReader("geym.jvm.ch10.asm.Account"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES); AddSecurityCheckClassAdapter classAdapter = new AddSecurityCheckClassAdapter(cw); cr.accept(classAdapter, ClassReader.SKIP_DEBUG); byte[] data = cw.toByteArray(); File file = new File("bin/geym/jvm/ch10/asm/Account.class"); FileOutputStream fout = new FileOutputStream(file); fout.write(data); fout.close(); } } 控制台输出: SecurityChecker.checkSecurity ... operation....
5、JIT及其相关参数
1)字节码执行性能较差,所以可以对于热点代码编译成机器码再执行,在运行时的编译,叫做JIT Just-In-Time 2)JIT的基本思路是,将热点代码,就是执行比较频繁的代码,编译成机器码。
jvm参数配置 -Xint 解释执行 -Xcomp 全部编译执行 -Xmixed 默认,混合