深入JVM内核(十)——字节码执行

谁借莪1个温暖的怀抱¢ 2022-05-16 08:42 363阅读 0赞

由于之前看的容易忘记,因此特记录下来,以便学习总结与更好理解,该系列博文也是第一次记录,所有有好多不完善之处请见谅与留言指出,如果有幸大家看到该博文,希望报以参考目的看浏览,如有错误之处,谢谢大家指出与留言。

目录

1、javap

2、简单的字节码执行过程

3、常用的字节码

4、使用ASM生成Java字节码

5、JIT及其相关参数

6、总结—路漫漫其修远兮 吾将上下而求索


1、javap

  1. class文件反汇编工具
  2. ![70][]
  3. javap verbose Calc
  4. public int calc();
  5. Code:
  6. Stack=2, Locals=4, Args_size=1
  7. 0: sipush 500
  8. 3: istore_1
  9. 4: sipush 200
  10. 7: istore_2
  11. 8: bipush 50
  12. 10: istore_3
  13. 11: iload_1
  14. 12: iload_2
  15. 13: iadd
  16. 14: iload_3
  17. 15: idiv
  18. 16: ireturn
  19. }

2、简单的字节码执行过程

70 1

70 2

70 3

70 4

70 5

70 6

70 7

70 8

1、void setAge(int) 方法的字节码

2、2A 1B B5 00 20 B1

3、2A _aload_0

  1. 无参
  2. 将局部变量slot0 作为引用 压入操作数栈

4、1B _iload_1

  1. 无参
  2. 将局部变量slot1 作为整数 压入操作数栈

5、B5 _putfield

  1. 设置对象中字段的值
  2. 参数为2bytes (00 20) (指明了字段)
  3. 指向常量池的引用
  4. Constant\_Fieldref
  5. 此处为User.age
  6. 弹出栈中2个对象:objectref, value
  7. 将栈中的value赋给objectref的给定字段

6、B1 _return

3、常用的字节码

1、常量入栈

  1. ![70 9][]

2、局部变量压栈

  1. xload(xi l f d a)
  2. 分别表示intlongfloatdoubleobject ref
  3. xload\_n(n0 1 2 3)
  4. xaload(xi l f d a b c s)
  5. 分别表示int, long, float, double, obj ref ,byte,char,short
  6. 从数组中取得给定索引的值,将该值压栈
  7. iaload
  8. 执行前,栈:..., arrayref, index
  9. 它取得arrayref所在数组的index的值,并将值压栈
  10. 执行后,栈:..., value

3、出栈装载入局部变量

  1. xstore(xi l f d a)
  2. 出栈,存入局部变量
  3. xstore\_n(n 0 1 2 3)
  4. 出栈,将值存入第n个局部变量
  5. xastore(xi l f d a b c s)
  6. 将值存入数组中
  7. iastore
  8. 执行前,栈:...,arrayref, index, value
  9. 执行后,栈:...
  10. value存入arrayref\[index\]

4、通用栈操作(无类型)

  1. nop
  2. pop
  3. 弹出栈顶1个字长
  4. dup
  5. 复制栈顶1个字长,复制内容压入栈

5、类型转化

  1. ![70 10][]

6、i2l

  1. int转为long
  2. 执行前,栈:..., value
  3. 执行后,栈:...,result.word1,result.word2
  4. 弹出int,扩展为long,并入栈

7、整数运算

  1. ![70 11][]

8、浮点运算

  1. ![70 12][]

9、对象操作指令

  1. new
  2. getfield
  3. putfield
  4. getstatic
  5. putstatic

10、条件控制

  1. ifeq 如果为0,则跳转
  2. ifne 如果不为0,则跳转
  3. iflt 如果小于0 ,则跳转
  4. ifge 如果大于0,则跳转
  5. if\_icmpeq 如果两个int相同,则跳转

11、ifeq

  1. 参数 byte1,byte2
  2. value出栈 ,如果栈顶value0则跳转到(byte1<<8)|byte2
  3. 执行前,栈:...,value
  4. 执行后,栈:...

12、方法调用

  1. invokevirtual
  2. invokespecial
  3. invokestatic
  4. invokeinterface
  5. xreturn(x i l f d a 或为空)

4、使用ASM生成Java字节码

1、ASM

  1. Java字节码操作框架
  2. 可以用于修改现有类或者动态产生新类
  3. 用户
  4. AspectJ
  5. Clojure
  6. Ecplise
  7. spring
  8. cglib
  9. hibernate
  10. ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
  11. cw.visit(V1_7, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
  12. MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  13. mw.visitVarInsn(ALOAD, 0); //this 入栈
  14. mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
  15. mw.visitInsn(RETURN);
  16. mw.visitMaxs(0, 0);
  17. mw.visitEnd();
  18. mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
  19. mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
  20. mw.visitLdcInsn("Hello world!");
  21. mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
  22. mw.visitInsn(RETURN);
  23. mw.visitMaxs(0,0);
  24. mw.visitEnd();
  25. byte[] code = cw.toByteArray();
  26. AsmHelloWorld loader = new AsmHelloWorld();
  27. Class exampleClass = loader
  28. .defineClass("Example", code, 0, code.length);
  29. exampleClass.getMethods()[0].invoke(null, new Object[] { null });

2、模拟实现AOP字节码织入

  1. 在函数开始部分或者结束部分嵌入字节码
  2. 可用于进行鉴权、日志等

70 13

我们需要嵌入的内容

  1. ![70 14][]

3、

  1. class AddSecurityCheckClassAdapter extends ClassVisitor {
  2. public AddSecurityCheckClassAdapter( ClassVisitor cv) {
  3. super(Opcodes.ASM5, cv);
  4. }
  5. // 重写 visitMethod,访问到 "operation" 方法时,
  6. // 给出自定义 MethodVisitor,实际改写方法内容
  7. public MethodVisitor visitMethod(final int access, final String name,
  8. final String desc, final String signature, final String[] exceptions) {
  9. MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);
  10. MethodVisitor wrappedMv = mv;
  11. if (mv != null) {
  12. // 对于 "operation" 方法
  13. if (name.equals("operation")) {
  14. // 使用自定义 MethodVisitor,实际改写方法内容
  15. wrappedMv = new AddSecurityCheckMethodAdapter(mv);
  16. }
  17. }
  18. return wrappedMv;
  19. }
  20. }
  21. class AddSecurityCheckMethodAdapter extends MethodVisitor {
  22. public AddSecurityCheckMethodAdapter(MethodVisitor mv) {
  23. super(Opcodes.ASM5,mv);
  24. }
  25. public void visitCode() {
  26. visitMethodInsn(Opcodes.INVOKESTATIC, "geym/jvm/ch10/asm/SecurityChecker",
  27. "checkSecurity", "()Z");
  28. super.visitCode();
  29. }
  30. }
  31. public class Generator{
  32. public static void main(String args[]) throws Exception {
  33. ClassReader cr = new ClassReader("geym.jvm.ch10.asm.Account");
  34. ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
  35. AddSecurityCheckClassAdapter classAdapter = new AddSecurityCheckClassAdapter(cw);
  36. cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
  37. byte[] data = cw.toByteArray();
  38. File file = new File("bin/geym/jvm/ch10/asm/Account.class");
  39. FileOutputStream fout = new FileOutputStream(file);
  40. fout.write(data);
  41. fout.close();
  42. }
  43. }

SecurityChecker.checkSecurity …

operation….

5、JIT及其相关参数

1、字节码执行性能较差,所以可以对于热点代码编译成机器码再执行,在运行时的编译, 叫做JIT Just-In-Time

2、JIT的基本思路是,将热点代码,就是执行比较频繁的代码,编译成机器码。

70 15

70 16

70 17

70 18

3、JIT

  1. public class JITTest {
  2. public static void met(){
  3. int a=0,b=0;
  4. b=a+b;
  5. }
  6. public static void main(String[] args) {
  7. for(int i=0;i<1000;i++){
  8. met();
  9. }
  10. }
  11. }

-XX:CompileThreshold=1000

-XX:+PrintCompilation

70 19

4、-Xint

  1. 解释执行

5、-Xcomp

  1. 全部编译执行

6、-Xmixed

  1. 默认,混合

发表评论

表情:
评论列表 (有 0 条评论,363人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JVM—虚拟机字节执行引擎

    执行引擎是Java虚拟机最为核心的组成部分之一. 虚拟机是一个相对于物理机的概念, 两种及其都有代码执行能力, 其区别是物理机的执行引擎是直接建立在处理器, 硬件, 指令集和操