java 字節碼
類方法的方法參數從0開始,而實例方法的第0個參數是this指針。
For class methods (i.e. static methods) the method parameters start from zero, however, for instance methods the zero slot is reserved for this.
局部變量,基本類型有8種,還有引用類型和返回地址
A local variable can be:
- boolean
- byte
- char
- long
- short
- int
- float
- double
- reference
- returnAddress
All types take a single slot in the local variable array except long and double which both take two consecutive slots because these types are double width (64-bit instead of 32-bit).
基本類型除了long和double佔兩個slot之外,其餘的都只佔一個slot
The value of the new variable is then stored into the local variables array in the correct slot. If the variable is not a primitive value then the local variable slot only stores a reference. The reference points to an the object stored in the heap.
變量存儲在局部變量數組中,如果不是基本類型,則在slot中存儲一個對象的引用。
舉個栗子:
int i = 5;
Is compile to:
0: bipush 5
2: istore_0
bipush將整數添加到操作棧中
Is used to add a byte as an integer to the operand stack, in this case 5 as added to the operand stack.
istore_0存儲一個整數到局部變量中,只能是istore_0,istore_1,istore_2或者i_store3
Is one of a group of opcodes with the format istore_<n> they all store an integer into local variables. The <n> refers to the location in the local variable array that is being stored and can only be 0, 1, 2 or 3. Another opcode is used for values higher then 3 called istore, which takes an operand for the location in the local variable array.
2 A field (or class variable) is stored on the heap as part of a class instance (or object). Information about the field is added into the field_info array in the class file as shown below.
ClassFile {
u4 magic;魔數
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info contant_pool[constant_pool_count – 1];
u2 access_flags;標記類的public/protected/private
u2 this_class;當前類
u2 super_class;父類
u2 interfaces_count;接口數量
u2 interfaces[interfaces_count];
u2 fields_count;成員變量個數
field_info fields[fields_count];
u2 methods_count;方法個數
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
初始化類成員變量的操作,會自動添加到初始化方法體內
public class SimpleClass {
public int simpleField = 100;
}
An extra section appears when you run javap demonstrating the field added to the field_info array:
public int simpleField;
Signature: I
flags: ACC_PUBLIC
The byte code for the initialization is added into the constructor (shown in bold), as follows:
public SimpleClass();
Signature: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 100
7: putfield #2 // Field simpleField:I
10: return
字節碼中的data放在常量池中
Constant pool:
#1 = Methodref #4.#16 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#17 // SimpleClass.simpleField:I
#3 = Class #13 // SimpleClass
#4 = Class #19 // java/lang/Object
#5 = Utf8 simpleField
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 SimpleClass
#14 = Utf8 SourceFile
#15 = Utf8 SimpleClass.java
#16 = NameAndType #7:#8 // "<init>":()V
#17 = NameAndType #5:#6 // simpleField:I
#18 = Utf8 LSimpleClass;
#19 = Utf8 java/lang/Object
類常量定義
For example:
public class SimpleClass {
public final int simpleField = 100;
}
The field description is augmented with ACC_FINAL:
public static final int simpleField = 100;
Signature: I
flags: ACC_PUBLIC, ACC_FINAL
ConstantValue: int 100
The initialization in the constructor is however unaffected:
4: aload_0
5: bipush 100
7: putfield #2
靜態變量初始化,是放在類初始化器裏面的,使用的是cinit
Static Variables
A static class variable with the static modifier is flagged as ACC_STATIC in the class file as follows:
public static int simpleField;
Signature: I
flags: ACC_PUBLIC, ACC_STATIC
The byte code for initialization of static variables is not found in the instance constructor<init>. Instead static fields are initialized as part of the class constructor <cinit>using theputstatic operand instead of putfield operand.
static {};
Signature: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 100
2: putstatic #2 // Field simpleField:I
5: return
條件語句
if-else
The following code example shows a simple if-else comparing two integer parameters.
public int greaterThen(int intOne, int intTwo) {
if (intOne > intTwo) {
return 0;
} else {
return 1;
}
}
This method results in the following byte code:
0: iload_1
1: iload_2
2: if_icmple 7
5: iconst_0
6: ireturn
7: iconst_1
8: ireturn
First the two parameters are loaded onto the operand stack using iload_1 and iload_2.if_icmple then compares the top two values on the operand stack. This operand branches to byte code 7 if intOne is less then or equal to intTwo.
更復雜一點的例子
public int greaterThen(float floatOne, float floatTwo) {
int result;
if (floatOne > floatTwo) {
result = 1;
} else {
result = 2;
}
return result;
}
This method results in the following byte code:
0: fload_1
1: fload_2
2: fcmpl
3: ifle 11
6: iconst_1
7: istore_3
8: goto 13
11: iconst_2
12: istore_3
13: iload_3
14: ireturn
首先使用fcmpl得出兩個數的比較結果,然後將該結果放到操作棧中
因爲fcmpl is first used to compare floatOne and floatTwo and push the result onto the operand stack as follows:
- floatOne > floatTwo –> 1
- floatOne = floatTwo –> 0
- floatOne < floatTwo –> -1
- floatOne or floatTwo = NaN –> 1
Next ifle is used to branch to byte code 11 if the result from fcmpl is <= 0.
iload_3 is then used to push the result stored in the third local variable slot to the top of the operand stack so that it can be returned by the return instruction.
比較操作
if_icmp<cond>
eq
ne
lt
le
gt
ge
This group of opcodes are used to compare the top two integers on the operand stack and branch to a new byte code. The <cond> can be:
- eq - equals
- ne - not equals
- lt - less then
- le - less then or equal
- gt - greater then
- ge - greater then or equal
if_acmp<cond>
eq
ne
These two opcodes are used to test if two references are eq equal or ne non equal and branch to a new byte code location as specified by the operand.
ifnonnull 是否空
ifnull
These two opcodes are used to test if two references are null or not null and branch to a new byte code location as specified by the operand.
lcmp
This opcode is used to compare the top two integers on the operand stack and push a value onto the operand stack as follows:
- if value1 > value2 –> push 1
- if value1 = value2 –> push 0
- if value1 < value2 –> push -1
fcmp<cond>
l
g
dcmp<cond>
l
g
This group of opcodes is used to compare two floator double values and push a value onto the operand stack as follows:
- if value1 > value2 –> push 1
- if value1 = value2 –> push 0
- if value1 < value2 –> push -1
instanceof
This opcode pushes an int result of 1 onto the operand stack if the object at the top of the operand stack is an instance of the class specified. The operand for this opcode is used to specify the class by providing an index into the constant pool. If the object is null or not an instance of the specified class then the int result 0 is added to the operand stack.
switch操作,jdk7之前的,只支持能夠自動提升爲int的類型,如byte、char、short,int。jdk7之後開始支持String
public int simpleSwitch(int intOne) {
switch (intOne) {
case 0:
return 3;
case 1:
return 2;
case 4:
return 1;
default:
return -1;
}
}
This produces the following byte code:
0: iload_1
1: tableswitch {
default: 42
min: 0 這裏會計算出條件語句的最值,這樣對於不在min~max範圍的情況,直接跳到default.
max: 4
0: 36
1: 38
2: 42 // The tableswitch instruction also has values for 2 and 3, as these are not provided as casestatements in the Java code they both point to // the default code block.
3: 42
4: 40
}
36: iconst_3
37: ireturn
38: iconst_2
39: ireturn
40: iconst_1
41: ireturn
42: iconst_m1
43: ireturn
對於條件語句比較稀疏的,使用tableswithch太費空間,於是使用lookupswitch來查找分支,速度比tableswitch慢
public int simpleSwitch(int intOne) { switch (intOne) { case 10: return 1; case 20: return 2; case 30: return 3; default: return -1; } }
This produces the following byte code:
0: iload_1 1: lookupswitch { default: 42 count: 3 10: 36 20: 38 30: 40 } 36: iconst_1 37: ireturn 38: iconst_2 39: ireturn 40: iconst_3 41: ireturn 42: iconst_m1 43: ireturn
JDK7開始支持String作爲switch的條件。主要是通過對字符串的hash code進行比較,分爲兩個階段
首先是將操作棧頂的字符串的hash code和switch分支的hash code進行比較,然後使用tableswitch跳到正確的分支
public int simpleSwitch(String stringOne) { switch (stringOne) { case "a": return 0; case "b": return 2; case "c": return 3; default: return 4; } }
This String switch statement will produce the following byte code:
0: aload_1 1: astore_2 2: iconst_m1 3: istore_3 4: aload_2 5: invokevirtual #2 // Method java/lang/String.hashCode:()I 8: tableswitch { default: 75 min: 97 max: 99 97: 36 98: 50 99: 64 } 36: aload_2 37: ldc #3 // String a 39: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 42: ifeq 75 45: iconst_0 46: istore_3 47: goto 75 50: aload_2 51: ldc #5 // String b 53: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 56: ifeq 75 59: iconst_1 60: istore_3 61: goto 75 64: aload_2 65: ldc #6 // String c 67: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 70: ifeq 75 73: iconst_2 74: istore_3 75: iload_3 76: tableswitch { default: 110 min: 0 max: 2 0: 104 1: 106 2: 108 } 104: iconst_0 105: ireturn 106: iconst_2 107: ireturn 108: iconst_3 109: ireturn 110: iconst_4 111: ireturn該類的常量池如下:
Constant pool: #2 = Methodref #25.#26 // java/lang/String.hashCode:()I #3 = String #27 // a #4 = Methodref #25.#28 // java/lang/String.equals:(Ljava/lang/Object;)Z #5 = String #29 // b #6 = String #30 // c #25 = Class #33 // java/lang/String #26 = NameAndType #34:#35 // hashCode:()I #27 = Utf8 a #28 = NameAndType #36:#37 // equals:(Ljava/lang/Object;)Z #29 = Utf8 b #30 = Utf8 c #33 = Utf8 java/lang/String #34 = Utf8 hashCode #35 = Utf8 ()I #36 = Utf8 equals #37 = Utf8 (Ljava/lang/Object;)Z
難怪之前都不支持String的分支,果然是比較麻煩啊。。。
如果條件語句的字符串的hash code相等,那麼字節碼則改一下:
public int simpleSwitch(String stringOne) { switch (stringOne) { case "FB": return 0; case "Ea": return 2; default: return 4; } }
This generates the following byte code:
0: aload_1 1: astore_2 2: iconst_m1 3: istore_3 4: aload_2 5: invokevirtual #2 // Method java/lang/String.hashCode:()I 8: lookupswitch { default: 53 count: 1 2236: 28 } 28: aload_2 29: ldc #3 // String Ea 31: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 34: ifeq 42 37: iconst_1 38: istore_3 39: goto 53 42: aload_2 43: ldc #5 // String FB 45: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 48: ifeq 53 51: iconst_0 52: istore_3 53: iload_3 54: lookupswitch { default: 84 count: 2 0: 80 1: 82 } 80: iconst_0 81: ireturn 82: iconst_2 83: ireturn 84: iconst_4 85: ireturn
循環語句:
loop
while、do-while、for
循環語句中,一般有分支語句:such as if_icmpge or if_icmplt,and a goto statement.
舉個栗子
public void whileLoop() { int i = 0; while (i < 2) { i++; } }
Is compiled to:
0: iconst_0 1: istore_1 2: iload_1 3: iconst_2 4: if_icmpge 13 7: iinc 1, 1 //自增1 10: goto 2 13: return
The iinc instruction
is one of the few instruction that updates a local variable directly without having to load or store values in the operand stack. In this example the iincinstruction
increases the first local variable (i.e. i) by 1.
for循環和while循環類似
do-while循環,
public void doWhileLoop() { int i = 0; do { i++; } while (i < 2); }
Results in the following byte code:
0: iconst_0 //常量壓棧到操作棧 1: istore_1 //拋棧到local variables表中 2: iinc 1, 1 5: iload_1 //將local variables中元素壓棧, 6: iconst_2 //常量壓棧 7: if_icmplt 2 //從棧中取出兩個元素,比較大小,如果less than 10: return
To Be Continued...
參考點擊打開鏈接