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。