Java ByteCode

1,什么是Bytecode
C/C++
编译器把源代码编译成汇编代码,Java编译器把Java源代码编译成字节码bytecode
Java
跨平台其实就是基于相同的bytecode规范做不同平台的虚拟机,我们的Java程序编译成bytecode后就可以在不同平台跑了。
.net
框架有IL(intermediate language),汇编是C/C++程序的中间表达方式,而bytecode可以说是Java平台的中间语言。
了解Java字节码知识对debuggingperformance tuning以及做一些高级语言扩展或框架很有帮助。

2
,使用javap生成Bytecode
JDK
自带的javap.exe文件可以反汇编Bytecode,让我们看个例子:
Test.java:

Java代码

  1. public class Test {   
  2.   public static void main(String[] args) {   
  3.     int i = 10000;   
  4.      System.out.println("Hello Bytecode! Number = " + i);   
  5.    }   
  6. }  

public class Test {

  public static void main(String[] args) {

    int i = 10000;

    System.out.println("Hello Bytecode! Number = " + i);

  }

}


编译后的Test.class:

Java代码

  1. 漱壕   1 +   
  2.       
  3.   
  4.   
  5.   
  6.   
  7. <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V   
  8. SourceFile     Test.java   
  9.    ! " java/lang/StringBuilder Hello Bytecode! Number = # $ # % & ' ( ) * Test java/lang/Object java/lang/System out Ljava/io/PrintStream; append -(Ljava/lang/String;)Ljava/lang/StringBuilder; (I)Ljava/lang/StringBuilder; toString ()Ljava/lang/String; java/io/PrintStream println (Ljava/lang/String;)V !   
  10.         
  11.         *                      >     ' < Y                      

漱壕   1 +

            

      

   

   

   

             <init>   ()V   Code   LineNumberTable   main   ([Ljava/lang/String;)V 

SourceFile    Test.java   

     ! "   java/lang/StringBuilder   Hello Bytecode! Number =   # $  # %  & '  (  ) *   Test   java/lang/Object   java/lang/System   out   Ljava/io/PrintStream;   append  -(Ljava/lang/String;)Ljava/lang/StringBuilder;   (I)Ljava/lang/StringBuilder;   toString   ()Ljava/lang/String;   java/io/PrintStream   println   (Ljava/lang/String;)V !

            

                *                                >         ' <    Y                                               


使用javap -c Test > Test.bytecode生成的Test.bytecode:

Java代码

  1. Compiled from "Test.java"  
  2. public class Test extends java.lang.Object{   
  3. public Test();   
  4.    Code:   
  5.    0:   aload_0   
  6.    1:   invokespecial   #1; //Method java/lang/Object."<init>":()V   
  7.    4:  return  
  8.   
  9. public static void main(java.lang.String[]);   
  10.    Code:   
  11.    0:   sipush  10000  
  12.    3:   istore_1   
  13.    4:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;   
  14.    7:  new   #3; //class java/lang/StringBuilder   
  15.    10:   dup   
  16.    11:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V   
  17.    14:   ldc   #5; //String Hello Bytecode! Number =   
  18.    16:   invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;   
  19.    19:   iload_1   
  20.    20:   invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;   
  21.    23:   invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;   
  22.    26:   invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V   
  23.    29:  return  
  24.   
  25. }  

Compiled from "Test.java"

public class Test extends java.lang.Object{

public Test();

  Code:

   0:  aload_0

   1:  invokespecial  #1; //Method java/lang/Object."<init>":()V

   4:  return

 

public static void main(java.lang.String[]);

  Code:

   0:  sipush  10000

   3:  istore_1

   4:  getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream;

   7:  new  #3; //class java/lang/StringBuilder

   10:  dup

   11:  invokespecial  #4; //Method java/lang/StringBuilder."<init>":()V

   14:  ldc  #5; //String Hello Bytecode! Number =

   16:  invokevirtual  #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

   19:  iload_1

   20:  invokevirtual  #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

   23:  invokevirtual  #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

   26:  invokevirtual  #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

   29:  return

 

}


JVM
就是一个基于stack的机器,每个thread拥有一个存储着一些framesJVM stack,每次调用一个方法时生成一个frame
一个frame包括一个local variables数组(本地变量表),一个Operand LIFO stack和运行时常量池的一个引用。

我们来简单分析一下生成的字节码指令:
aload
iload指令的“a”前缀和“i”分别表示对象引用和int类型,其他还有“b”表示byte“c”表示char“d”表示double等等
我们这里的aload_0表示将把local variable tableindex 0的值pushOperand stackiload_1类似
invokespecial
表示初始化对象,return表示返回
sipush
表示把10000这个intpushOperand stack
getstatic
表示取静态域
invokevirtual
表示调用一些实例方法
这些指令又称为opcodeJava一直以来只有约202Opcode,具体请参考Java Bytecode规范。

我们看到Test.class文件不全是二进制的指令,有些是我们可以识别的字符,这是因为有些包名、类名和常量字符串没有编译成二进制Bytecode指令。

3
,体验字节码增强的魔力
我们J2EE常用的HibernateSpring都用到了动态字节码修改来改变类的行为。
让我们通过看看ASMorg.objectweb.asm.MethodWriter类的部分方法来理解ASM是如何修改字节码的:

Java代码

  1. class MethodWriter implements MethodVisitor {   
  2.   
  3.     private ByteVector code = new ByteVector();   
  4.   
  5.     public void visitIntInsn(final int opcode, final int operand) {   
  6.         // Label currentBlock = this.currentBlock;   
  7.         if (currentBlock != null) {   
  8.             if (compute == FRAMES) {   
  9.                  currentBlock.frame.execute(opcode, operand, null, null);   
  10.              } else if (opcode != Opcodes.NEWARRAY) {   
  11.                 // updates current and max stack sizes only for NEWARRAY   
  12.                 // (stack size variation = 0 for BIPUSH or SIPUSH)   
  13.                 int size = stackSize + 1;   
  14.                 if (size > maxStackSize) {   
  15.                      maxStackSize = size;   
  16.                  }   
  17.                  stackSize = size;   
  18.              }   
  19.          }   
  20.         // adds the instruction to the bytecode of the method   
  21.         if (opcode == Opcodes.SIPUSH) {   
  22.              code.put12(opcode, operand);   
  23.          } else { // BIPUSH or NEWARRAY   
  24.              code.put11(opcode, operand);   
  25.          }   
  26.      }   
  27.   
  28.     public void visitMethodInsn(   
  29.         final int opcode,   
  30.         final String owner,   
  31.         final String name,   
  32.         final String desc)   
  33.      {   
  34.         boolean itf = opcode == Opcodes.INVOKEINTERFACE;   
  35.          Item i = cw.newMethodItem(owner, name, desc, itf);   
  36.         int argSize = i.intVal;   
  37.         // Label currentBlock = this.currentBlock;   
  38.         if (currentBlock != null) {   
  39.             if (compute == FRAMES) {   
  40.                  currentBlock.frame.execute(opcode, 0, cw, i);   
  41.              } else {   
  42.                 /*
  43.                   * computes the stack size variation. In order not to recompute
  44.                   * several times this variation for the same Item, we use the
  45.                   * intVal field of this item to store this variation, once it
  46.                   * has been computed. More precisely this intVal field stores
  47.                   * the sizes of the arguments and of the return value
  48.                   * corresponding to desc.
  49.                   */  
  50.                 if (argSize == 0) {   
  51.                     // the above sizes have not been computed yet,   
  52.                     // so we compute them...   
  53.                      argSize = getArgumentsAndReturnSizes(desc);   
  54.                     // ... and we save them in order   
  55.                     // not to recompute them in the future   
  56.                      i.intVal = argSize;   
  57.                  }   
  58.                 int size;   
  59.                 if (opcode == Opcodes.INVOKESTATIC) {   
  60.                      size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;   
  61.                  } else {   
  62.                      size = stackSize - (argSize >> 2) + (argSize & 0x03);   
  63.                  }   
  64.                 // updates current and max stack sizes   
  65.                 if (size > maxStackSize) {   
  66.                      maxStackSize = size;   
  67.                  }   
  68.                  stackSize = size;   
  69.              }   
  70.          }   
  71.         // adds the instruction to the bytecode of the method   
  72.         if (itf) {   
  73.             if (argSize == 0) {   
  74.                  argSize = getArgumentsAndReturnSizes(desc);   
  75.                  i.intVal = argSize;   
  76.              }   
  77.              code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);   
  78.          } else {   
  79.              code.put12(opcode, i.index);   
  80.          }   
  81.      }   
  82. }  

class MethodWriter implements MethodVisitor {

 

    private ByteVector code = new ByteVector();

 

    public void visitIntInsn(final int opcode, final int operand) {

        // Label currentBlock = this.currentBlock;

        if (currentBlock != null) {

            if (compute == FRAMES) {

                currentBlock.frame.execute(opcode, operand, null, null);

            } else if (opcode != Opcodes.NEWARRAY) {

                // updates current and max stack sizes only for NEWARRAY

                // (stack size variation = 0 for BIPUSH or SIPUSH)

                int size = stackSize + 1;

                if (size > maxStackSize) {

                    maxStackSize = size;

                }

                stackSize = size;

            }

        }

        // adds the instruction to the bytecode of the method

        if (opcode == Opcodes.SIPUSH) {

            code.put12(opcode, operand);

        } else { // BIPUSH or NEWARRAY

            code.put11(opcode, operand);

        }

    }

 

    public void visitMethodInsn(

        final int opcode,

        final String owner,

        final String name,

        final String desc)

    {

        boolean itf = opcode == Opcodes.INVOKEINTERFACE;

        Item i = cw.newMethodItem(owner, name, desc, itf);

        int argSize = i.intVal;

        // Label currentBlock = this.currentBlock;

        if (currentBlock != null) {

            if (compute == FRAMES) {

                currentBlock.frame.execute(opcode, 0, cw, i);

            } else {

                /*

                 * computes the stack size variation. In order not to recompute

                 * several times this variation for the same Item, we use the

                 * intVal field of this item to store this variation, once it

                 * has been computed. More precisely this intVal field stores

                 * the sizes of the arguments and of the return value

                 * corresponding to desc.

                 */

                if (argSize == 0) {

                    // the above sizes have not been computed yet,

                    // so we compute them...

                    argSize = getArgumentsAndReturnSizes(desc);

                    // ... and we save them in order

                    // not to recompute them in the future

                    i.intVal = argSize;

                }

                int size;

                if (opcode == Opcodes.INVOKESTATIC) {

                    size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;

                } else {

                    size = stackSize - (argSize >> 2) + (argSize & 0x03);

                }

                // updates current and max stack sizes

                if (size > maxStackSize) {

                    maxStackSize = size;

                }

                stackSize = size;

            }

        }

        // adds the instruction to the bytecode of the method

        if (itf) {

            if (argSize == 0) {

                argSize = getArgumentsAndReturnSizes(desc);

                i.intVal = argSize;

            }

            code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);

        } else {

            code.put12(opcode, i.index);

        }

    }

}

1.       通过注释我们可以大概理解visitIntInsnvisitMethodInsn方法的意思。
比如visitIntInsn先计算stacksize,然后根据opcode来判断是SIPUSH指令还是BIPUSH or NEWARRAY指令,并相应的调用字节码修改相关的方法。

出自:http://hi.baidu.com/yxl1982/blog/item/1d09124c26033bfcd62afc60.html

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐