JVM Instruction Set Example 1

JVM Instruction Set官方手册:jvms se7 chapter6

JVM Instruction Wiki: Java bytecode instruction listings

手册看看单个指令还是好理解的,下面看看一些例子,其中涉及的命令有:

1
2
3
$javac Test.java          // compiler
$javap -c Test.class      // disassembler
$java Test                // run

Example 1.1

1
2
3
4
5
6
    void f1() {
	int i = 0;
	int j = 0;
	int z = i + j;
	z++;
    }
1
2
3
4
5
6
7
8
9
10
11
12
  void f1();
    Code:                         // 以下局部变量数组是当前栈帧的局部变量数组
       0: iconst_0                // int型常量0入栈
       1: istore_1                // 栈顶int型值写入局部变量数组的第一个元素(i=0)
       2: iconst_0      
       3: istore_2                // 栈顶int型值写入局部变量数组的第二个元素(j=0)
       4: iload_1                 // 局部变量数组的第一个元素入栈,整型
       5: iload_2                 // 局部变量数组的第二个元素入栈,整型
       6: iadd                    // 取出栈顶两个int型元素,相加,结果入栈
       7: istore_3                // 栈顶int型值写入局部变量数组的第二个元素(z=i+j)
       8: iinc          3, 1      // 局部变量数组的第三个元素加1
      11: return

Example 1.2

1
2
3
4
5
6
    void f2() {
	Integer i = 100;
	Integer j = new Integer(100);
	Integer z = i + j;
	z++;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void f2();
    Code:
       0: bipush        100        // byte类型100入栈顶,byte类型符号扩展成int
       2: invokestatic  #7         // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;自动装箱,结果在栈顶,是ref类型
       5: astore_1                 // 栈顶ref类型值写入局部变量数组的第1个元素(Integer i = 100;)
	   
	   
       6: new           #8         // class java/lang/Integer;创建instance,在heap中为此instance申请空间,其成员域初使化为默认值,向栈顶压入其ref
       9: dup                      // 复制栈顶元素,并压入栈顶
      10: bipush        100
      12: invokespecial #9         // Method java/lang/Integer."<init>":(I)V;调用Integer的初使化函数init(ref, 100),将栈顶两个元素作为参数;dup拷贝的ref在这里使用掉了
      15: astore_2                 // 栈顶ref类型值写入局部变量数组的第2个元素(Integer j = new Integer(100););这个ref是dup前存在的ref
	  
	  
      16: aload_1                  // i的ref类型入栈
      17: invokevirtual #10        // Method java/lang/Integer.intValue:()I;自动拆箱,返回int类型值入栈
      20: aload_2       
      21: invokevirtual #10        // Method java/lang/Integer.intValue:()I
      24: iadd                     // 取出栈顶两个int元素,相加,入栈
      25: invokestatic  #7         // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;自动装箱
      28: astore_3                 // (Integer z = i + j;)   
	  
	  
      29: aload_3                  
      30: astore        4          // ? z的ref备份到局部变量数组的第4个位置,为了能够++先返回其原先值??
      32: aload_3                  
      33: invokevirtual #10        // Method java/lang/Integer.intValue:()I;z自动拆箱
      36: iconst_1      
      37: iadd          
      38: invokestatic  #7         // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;自动装箱,
      41: dup           
      42: astore_3                 // z指向了另一个instance,即更改了ref(z++;)
      43: astore        5          // ? 下面感觉没什么用了
      45: aload         4
      47: pop           
      48: return

第一个指令bipush会做一个自动类型提升。 JVM定义如下自动类型提升:

  • byte型,short型,char型会被提升到int型。
  • 算数表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型。

Example 1.3

这个例子是一个经典笔试题了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    void f3() {
	Integer i1 = 100;
	Integer i2 = 100;
	Integer i3 = 0;
	Integer i4 = new Integer(100);
	Integer i5 = new Integer(100);
	Integer i6 = new Integer(0);
	
	System.out.println(i1 == i2);
	System.out.println(i1 == i2 + i3);
	System.out.println(i4 == i5);
	System.out.println(i4 == i5 + i6);

	Integer i7 = new Integer(200);
	Integer i8 = new Integer(200);
	System.out.println(i7 == i8);	
	System.out.println(i7 == i4 + i5);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  void f3();
    Code:
       0: bipush        100
       2: invokestatic  #7    // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1            // (i1 = 100;)
	   
	   
       6: bipush        100
       8: invokestatic  #7    // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      11: astore_2            // (i2 = 100;)
	  
	  
      12: iconst_0      
      13: invokestatic  #7    // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      16: astore_3            // (i3 = 0;)
	  
	  
      17: new           #8    // class java/lang/Integer
      20: dup           
      21: bipush        100
      23: invokespecial #9    // Method java/lang/Integer."<init>":(I)V
      26: astore        4     // (i4 = new Integer(100);)
	  
	  
      28: new           #8    // class java/lang/Integer
      31: dup           
      32: bipush        100
      34: invokespecial #9    // Method java/lang/Integer."<init>":(I)V
      37: astore        5     // (i5 = new Integer(100);)
	  
	  
      39: new           #8    // class java/lang/Integer	
      42: dup           
      43: iconst_0      
      44: invokespecial #9    // Method java/lang/Integer."<init>":(I)V
      47: astore        6     // (i6 = new Integer(0);)
	  
	  
      49: getstatic     #11   // Field java/lang/System.out:Ljava/io/PrintStream;
      52: aload_1       
      53: aload_2       
      54: if_acmpne     61     // (i1 == i2)比较的ref值是否相等,即是否引用同一对象;不相等则话跳转至61
      57: iconst_1             
      58: goto          62
      61: iconst_0      
      62: invokevirtual #12    // Method java/io/PrintStream.println:(Z)V
	  
	  
      65: getstatic     #11    // Field java/lang/System.out:Ljava/io/PrintStream;
      68: aload_1       
      69: invokevirtual #10    // Method java/lang/Integer.intValue:()I
      72: aload_2       
      73: invokevirtual #10    // Method java/lang/Integer.intValue:()I
      76: aload_3       
      77: invokevirtual #10    // Method java/lang/Integer.intValue:()I
      80: iadd                 // 都拆箱,int相加
      81: if_icmpne     88     // (i1 == i2 + i3),直接比较的int值
      84: iconst_1      
      85: goto          89
      88: iconst_0      
      89: invokevirtual #12    // Method java/io/PrintStream.println:(Z)V
	  
	  
      92: getstatic     #11    // Field java/lang/System.out:Ljava/io/PrintStream;
      95: aload         4
      97: aload         5
      99: if_acmpne     106    // (i4 == i5),比较的ref值
     102: iconst_1      
     103: goto          107
     106: iconst_0      
     107: invokevirtual #12     // Method java/io/PrintStream.println:(Z)V
	 
	 
     110: getstatic     #11     // Field java/lang/System.out:Ljava/io/PrintStream;
     113: aload         4
     115: invokevirtual #10     // Method java/lang/Integer.intValue:()I
     118: aload         5
     120: invokevirtual #10     // Method java/lang/Integer.intValue:()I
     123: aload         6
     125: invokevirtual #10     // Method java/lang/Integer.intValue:()I
     128: iadd          
     129: if_icmpne     136     // (i4 == i5 + i6),比较的int值
     132: iconst_1      
     133: goto          137
     136: iconst_0      
     137: invokevirtual #12     // Method java/io/PrintStream.println:(Z)V
	 
	 
     140: new           #8      // class java/lang/Integer
     143: dup           
     144: sipush        200
     147: invokespecial #9      // Method java/lang/Integer."<init>":(I)V
     150: astore        7       // (i7 = new Integer(200);)
	 
	 
     152: new           #8      // class java/lang/Integer
     155: dup           
     156: sipush        200
     159: invokespecial #9      // Method java/lang/Integer."<init>":(I)V
     162: astore        8       // (i8 = new Integer(200);)


     164: getstatic     #11     // Field java/lang/System.out:Ljava/io/PrintStream;
     167: aload         7
     169: aload         8
     171: if_acmpne     178     // (i7 == i8),比较的是ref值
     174: iconst_1      
     175: goto          179
     178: iconst_0      
     179: invokevirtual #12     // Method java/io/PrintStream.println:(Z)V
	 
	 
     182: getstatic     #11     // Field java/lang/System.out:Ljava/io/PrintStream;
     185: aload         7
     187: invokevirtual #10     // Method java/lang/Integer.intValue:()I
     190: aload         4
     192: invokevirtual #10     // Method java/lang/Integer.intValue:()I
     195: aload         5
     197: invokevirtual #10     // Method java/lang/Integer.intValue:()I
     200: iadd          
     201: if_icmpne     208     // (i7 == i4 + i5), 比较的是int值
     204: iconst_1      
     205: goto          209
     208: iconst_0      
     209: invokevirtual #12     // Method java/io/PrintStream.println:(Z)V
     212: return

结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    void f3() {
	Integer i1 = 100;
	Integer i2 = 100;
	Integer i3 = 0;
	Integer i4 = new Integer(100);
	Integer i5 = new Integer(100);
	Integer i6 = new Integer(0);

	System.out.println(i1 == i2);           // true
	System.out.println(i1 == i2 + i3);      // true
	System.out.println(i4 == i5);           // false
	System.out.println(i4 == i5 + i6);      // true

	Integer i7 = new Integer(200);
	Integer i8 = new Integer(200);
	System.out.println(i7 == i8);           // false	
	System.out.println(i7 == i4 + i5);      // true
    }

这里还隐藏一个自动装箱时的优化。 自动装箱调用的是Integer.valueOf(int),查看源码:

1
2
3
4
5
6
    public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)  // high = 127
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }

这里会对值在-128~127的Integer做共享,相同的值共享同一对象。 因此其ref也相同。因此i1,i2指向同一对象,比较其ref时结果为true。

i3,i4,i7,i8直接创建了新的对象,指向了不同对象,ref不同。 因此i3==i4,i7==i8结果为false。

而其余几条都涉及代数计算,因此对象自动拆箱为基本类型进行计算, 结果也是比较其代数计算值的int类型结果。因此都为true。