每天学一点java字节码

java 字节码

类方法的方法参数从0开始,而实例方法的第0个参数是this指针。

Forclassmethods (i.e.staticmethods) the method parameters start from zero, however, for instance methods the zero slot is reserved forthis.

局部变量,基本类型有8种,还有引用类型和返回地址

A local variable can be:

booleanbytecharlongshortintfloatdoublereferencereturnAddress

All types take a single slot in the local variable array exceptlonganddoublewhich 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. Thereferencepoints 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 abyteas 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 calledistore, which takes an operand for the location in the local variable array.

2A field (or class variable) is stored on the heap as part of a class instance (or object). Information about the field is added into thefield_infoarray in the class file as shown below.

ClassFile {

u4magic;魔数

u2minor_version;

u2major_version;

u2constant_pool_count;

cp_infocontant_pool[constant_pool_count – 1];

u2access_flags;标记类的public/protected/private

u2this_class;当前类

u2super_class;父类

u2interfaces_count;接口数量

u2interfaces[interfaces_count];

u2fields_count;成员变量个数

field_infofields[fields_count];

u2methods_count;方法个数

method_infomethods[methods_count];

u2attributes_count;

attribute_infoattributes[attributes_count];

}

初始化类成员变量的操作,会自动添加到初始化方法体内

public class SimpleClass {

public int simpleField = 100;

}

An extra div appears when you runjavapdemonstrating the field added to the field_infoarray:

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 withACC_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

Astaticclass variable with thestaticmodifier is flagged asACC_STATICin theclass file as follows:

public static int simpleField;

Signature: I

flags: ACC_PUBLIC, ACC_STATIC

The byte code for initialization ofstaticvariables is not found in the instance constructor<init>. Insteadstaticfields are initialized as part of theclassconstructor<cinit>using theputstatic operand instead ofputfieldoperand.

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 simpleif-elsecomparing 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 usingiload_1andiload_2.if_icmplethen compares the top two values on the operand stack. This operand branches to byte code7if 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得出两个数的比较结果,然后将该结果放到操作栈中

因为fcmplis first used to compare floatOne and floatTwo and push the result onto the operand stack as follows:

floatOne>floatTwo–>1floatOne=floatTwo–>0floatOne<floatTwo–>-1floatOne or floatTwo=NaN–>1

Nextifleis used to branch to byte code11if the result fromfcmplis<=0.

iload_3is 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 thereturninstruction.

比较操作

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- equalsne- not equalslt- less thenle- less then or equalgt- greater thenge- greater then or equal

if_acmp<cond>

eq

ne

These two opcodes are used to test if two references areeqequal ornenon 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 arenullor notnulland 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 1if value1=value2–>push 0if value1<value2–>push -1

fcmp<cond>

l

g

dcmp<cond>

l

g

This group of opcodes is used to compare twofloatordoublevalues and push a value onto the operand stack as follows:

if value1>value2–>push 1if value1=value2–>push 0if value1<value2–>push -1

instanceof

This opcode pushes anintresult of 1 onto the operand stack if the object at the top of the operand stack is an instance of theclassspecified. The operand for this opcode is used to specify the class by providing an index into the constant pool. If the object isnullor not an instance of the specifiedclassthen theintresult 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 // Thetableswitchinstruction also has values for 2 and 3, as these are not provided ascasestatements in the Java code they both point to //thedefaultcode 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_137: ireturn38: iconst_239: ireturn40: iconst_341: ireturn42: iconst_m143: 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 Stringswitchstatement 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_237: ldc           #3                  // String a39: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z42: ifeq          7545: iconst_046: istore_347: goto          7550: aload_251: ldc           #5                  // String b53: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z56: ifeq          7559: iconst_160: istore_361: goto          7564: aload_265: ldc           #6                  // String c67: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z70: ifeq          7573: iconst_274: istore_375: iload_376: tableswitch   {         default: 110             min: 0             max: 2               0: 104               1: 106               2: 108       }104: iconst_0105: ireturn106: iconst_2107: ireturn108: iconst_3109: ireturn110: iconst_4111: 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_229: ldc           #3                  // String Ea31: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z34: ifeq          4237: iconst_138: istore_339: goto          5342: aload_243: ldc           #5                  // String FB45: invokevirtual #4                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z48: ifeq          5351: iconst_052: istore_353: iload_354: lookupswitch  {         default: 84           count: 2               0: 80               1: 82    }80: iconst_081: ireturn82: iconst_283: ireturn84: iconst_485: ireturn

循环语句:

loop

while、do-while、for

循环语句中,一般有分支语句:such asif_icmpgeorif_icmplt,and agotostatement.

举个栗子

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 //自增110: goto          213: return

Theiincinstruction 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 theiincinstruction 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 than10: return

To Be Continued…

参考点击打开链接

看了哪些风景,遇到哪些人。尽管同学说,去旅行不在于记忆,

每天学一点java字节码

相关文章:

你感兴趣的文章:

标签云: