zhaoyu@home:~$

jvm-编译

编译格式例子

如下面的代码:

void dspin(){
    double j;
    for (j = 0.0; j < 100.0; j++) {
        ;
    }
}

javap反编译之后得到:

dspin()V
  L0
    LINENUMBER 20 L0
    DCONST_0           //定义double常量0
    DSTORE 1           //存储到本地变量表1
  L1
  FRAME APPEND [D]
    DLOAD 1            //加载本地变量表1的值
    LDC 100.0          //加载常量100.0
    DCMPG              //栈顶下的元素和栈顶元素作比较
    IFGE L2            //如果大于等于,跳到L2结束循环。
    DLOAD 1            
    DCONST_1
    DADD               //栈顶的两个元素相加
    DSTORE 1
    GOTO L1
  L2
    LINENUMBER 23 L2
  FRAME SAME
    RETURN

取常量

iconst_1:取常量1。 iconst_m1:取常量-1。

访问运行时常量池

访问常量池的指令有ldc,ldc_w,ldc2w。
ldc和ldc_w用于访问除long和double外的常量类型,如int,String。ldc_w在有大量的运行时常量池项目并且需要一个大的索引访问项目 时使用。ldc2_w用于访问所有的double和long类型。
byte,char,short作为小整型值,可能使用bipush,sipush或iconst
指令。

类实例

创建一个实例的语句如下:

new TestFoo();

编译代码:

NEW net/zhaoyu/javapros/test/TestFoo 
DUP                                         
INVOKESPECIAL net/zhaoyu/javapros/test/TestFoo.<init> ()V

从上面的代码可以看出,java中new一个对象,需要执行3个步骤:1新建一个对象,2复制到栈顶,3调用构造方法,即init方法。

数组

有如下操作数组的代码:

void createBuffer() {
    int buffer[];
    int bufsz = 100;
    int value = 12;
    buffer = new int[bufsz];
    buffer[10] = value;
    value = buffer[11];
}

编译后如下:

Method void createBuffer()
0 bipush 100 
2 istore_2 
3 bipush 12 
5 istore_3 
6 iload_2 
7 newarray int // 创建数组,根据栈顶元素 
9 astore_1 // 保存数组到本地变量
10 aload_1 // 数组入栈
11 bipush 10 // 常量10入栈
13 iload_3 // value变量入栈
14 iastore // 将栈顶值保存为数组[10]
15 aload_1 // 数组重新入栈
16 bipush 11 // 常量11入栈
18 iaload // 将数组[11]入栈
19 istore_3
20 return

操作栈中的操作

假如有如下代码:

long l=2;
public long nextIndex(){
    return l++;
}

编译结果如下:

ALOAD 0     //this入栈
DUP         //复制栈顶元素并入栈,栈为this,this
GETFIELD net/zhaoyu/javapros/test/TestFoo.l : J //消耗一个this,获取l,此时栈为this,l。
DUP2_X1     //复制栈顶的l,并插入到this下面。此时栈为j,this,j。
LCONST_1    
LADD        // 栈顶2个元素相加,栈为j,this,j+1
PUTFIELD net/zhaoyu/javapros/test/TestFoo.l : J //j+1赋值给字段。即this.j=j+1。结束后栈为j
LRETURN     // 返回栈顶元素j。

DUP2_X1
可以理解为:将栈顶的2个Slot的值复制并插入到栈顶的3(2+1)个Slot的值下面,long和double占用2个slot,其他类型占用1个slot。

注意,JVM从不允许操作栈的操作指令修改和破坏操作栈中的变量,所以需要dup等操作。

throw和异常捕获

有如下代码:

void tryCatchTest(){
    try {
        nextIndex();
    } catch (Exception e) {
        throw new IllegalArgumentException();
    }
}

编译后的代码如下:

void tryCatchTest();
Code:
   0: aload_0
   1: invokevirtual #5                  // Method nextIndex:()J
   4: pop2
   5: goto          17                  //try执行完,跳到return
   8: astore_1                       // <catch块> 发生throw,则存储Exception
   9: new           #7                  // new IllegalArgumentException
  12: dup
  13: invokespecial #8                  // IllegalArgumentException."<init>":()V
  16: athrow                         // <catch块>结束
  17: return
Exception table:
   from    to  target type
       0     5     8   Class java/lang/Exception

在上面的编译代码中,异常的tryCatch会生成Exception table。异常表的[from-to)记录try的范围,target是catch的行数。

finally的处理有些特殊,finally会将代码分布到try块和catch块中。以确保一定执行。例如有代码如下:

void tryCatchTest(){
    try {
        nextIndex();
    } catch (Exception e) {
        throw new IllegalArgumentException();
    }finally {
        throw new RuntimeException();
    }
}

编译后的代码如下:

  void tryCatchTest();
  descriptor: ()V
  flags:
  Code:
    stack=2, locals=3, args_size=1
       0: aload_0
       1: invokevirtual #5                  // Method nextIndex:()J
       4: pop2
       5: new           #6                  // <final块> class java/lang/RuntimeException
       8: dup
       9: invokespecial #7                  // <final块> Method java/lang/RuntimeException."<init>":()V
      12: athrow
      13: astore_1                      //<catch块>开始 
      14: new           #9                  // class java/lang/IllegalArgumentException
      17: dup
      18: invokespecial #10                 // Method java/lang/IllegalArgumentException."<init>":()V
      21: athrow                     
      22: astore_2
      23: new           #6                  // <final块> class java/lang/RuntimeException
      26: dup
      27: invokespecial #7                  // <final块> Method java/lang/RuntimeException."<init>":()V
      30: athrow                        //<catch块>结束
    Exception table:
       from    to  target type
           0     5    13   Class java/lang/Exception
           0     5    22   any
          13    23    22   any

从编译代码中可以看出,finally语句会分别插入到try块和catch块中。并在异常表中插入两行。这两行用于捕获插入到try块和catch块中 的final块的异常。

同步

同步有两种实现方式,一种是在方法上隐式地加上ACC_SYNCHRONIZED。另一种是使用monitorentermonitorexitmonitorentermonitorexit指令会增加同步语句的复杂性,例如有如下程序:

void onlyMe(Foo f) {
    synchronized(f) {
        doSomething();
    }
}

编译后的代码如下:

0 aload_1             // Push f
1 dup                 // 
2 astore_2            // 
3 monitorenter        // 进入monitor,同步开始 
4 aload_0             // Holding the monitor, pass this and...
5 invokevirtual #5    // 
8 aload_2             // Push local variable 2 (f)
9 monitorexit         // 从f的monitor中退出。
10 goto 18            // goto return
13 astore_3           // 异常情况处理
14 aload_2            // Push local variable 2 (f)
15 monitorexit        // 确定异常情况下退出monitor
16 aload_3            
17 athrow             
18 return             // Return 

Exception table:
From To  Target Type
4    10  13     any
13   16  13     any

从上面的代码可以看出,程序自动为synchronized块添加了异常表处理。用于处理synchronized块,及异常后的代码块。