[介绍]
本文给出一个java字节码的例子。 纸上得来终觉浅,绝知此事要躬行。
本文将详细解释一个字节码例子。 看过一些相关的文章和书籍,但是给出的例子一般都更加简化,然后给出理论解释。
我自己通过认真分析此字节码,基本上把重要部分都cover了。
本文的例子来自于: [KB JVM - How to print readable bytecode from class file -> An example of bytecode - java]
[一些note]
关于字节码的一些note,copy到这里
java字节码与汇编程序(Assembly)有点类似.
区别在于:1)JVM没有寄存器,所有操作均在Stack上执行 2)
但是有局部变量表
load : slot# --> stackstore: slot# <-- stackconst: const --> stack
这里还有个简单的例子: [P161 Ref 1 zzm]
note:每个方法所使用的局部变量表、栈的大小在编译时确定
note:局部变量表 0 - this, 1..m m个方法参数,剩下的是可用的slot
note:调用某个方法之前,会将局部变量表准备好,因为一个线程中的所有方法共享同一个局部变量表
[Java Source Code]
public class Volatile
{
private volatile int field = 0;
public void increase(){
field++;
}
public static void main(String[] args){
Volatile ex = new Volatile();
ex.increase();
}
}
[ByteCode]
javap -verbose Volatile.class
下面的以BY:开头的是我的注释
都是用英文写的,因为我当时的编辑工具中文显示乱码。
D:\baoying_no_index\360CloudDisk_Compiling_Output\BaoyingCleanProject\bin>javap
-verbose Volatile
Compiled from "Volatile.java"
public class Volatile extends java.lang.Object
SourceFile: "Volatile.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #2; // Volatile
const #2 = Asciz Volatile;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz field;
const #6 = Asciz I;
const #7 = Asciz ;
const #8 = Asciz ()V;
const #9 = Asciz Code;
const #10 = Method #3.#11; // java/lang/Object."":()V
const #11 = NameAndType #7:#8;// "":()V
const #12 = Field #1.#13; // Volatile.field:I
const #13 = NameAndType #5:#6;// field:I
const #14 = Asciz LineNumberTable;
const #15 = Asciz LocalVariableTable;
const #16 = Asciz this;
const #17 = Asciz LVolatile;;
const #18 = Asciz increase;
const #19 = Asciz main;
const #20 = Asciz ([Ljava/lang/String;)V;
const #21 = Method #1.#11; // Volatile."":()V
const #22 = Method #1.#23; // Volatile.increase:()V
const #23 = NameAndType #18:#8;// increase:()V
const #24 = Asciz args;
const #25 = Asciz [Ljava/lang/String;;
const #26 = Asciz ex;
const #27 = Asciz SourceFile;
const #28 = Asciz Volatile.java;
BY: https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
{
public Volatile();
Code:
--BY: Stack=2 max Stack size
--BY: Locals=1, it means the Slot capacity is 1. It contains 'this' only.
--BY: Args_size=1. only 'this' as default parameter.
Stack=2, Locals=1, Args_size=1
0: aload_0 BY: push slot#0 ('this') to Stack. after Stack: this
1: invokespecial #10; //Method java/lang/Object."":()V BY: invoke parent constructor. After Stack: empty
4: aload_0 BY: push slot#0 ('this') to Stack. Why again? A: it is required by the following(#6) putfield.(20150630) After Stack: this
5: iconst_0 BY: push 0 to Stack. After Stack: (top)0, this (bottom)
6: putfield #12; //Field field:I BY: assign field#12 with 0 (const #12 = Field #1.#13; // Volatile.field:I). After Stack: empty.
9: return
LineNumberTable:
line 9: 0
line 11: 4
line 9: 9
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LVolatile;
// public void increase(){
// field++;
// }
public void increase();
Code:
Stack=3, Locals=1, Args_size=1
0: aload_0 BY: push 'this' to Stack. slot #0 is 'this'. why aload_0 is required?. A: It is used by the last ( line #7 put field) (20150630) . after: Stack:(top) this (bottom)
1: dup BY: dup the top of the stack, why dup is required?. A: it is used by the following(line #2) getfield. Although the getfile/putfied spec does not mention that it is required to put the 'this' side of the field, it is required! another example here http://cs.au.dk/~mis/dOvs/jvmspec/ref--18.html (20150630) after: Stack:(top)this, this (bottom)
2: getfield #12; //Field field:I BY: push the field #12 reference to Stack. after: Stack:(top)field #12, this(bottom)
5: iconst_1 BY: push 1 to Stack. Stack:(top)1, field #12, this, this(bottom)
6: iadd BY: pop 2 values and push adding result back.. after: Stack:(top) value of field #12+1, this(bottom)
7: putfield #12; //Field field:I BY: pop 2 values, set field #12 value. after: Stack:(top) (bottom)
10: return
LineNumberTable:
line 14: 0
line 15: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this LVolatile;
// public static void main(String[] args){
// Volatile ex = new Volatile();
// ex.increase();
// }
//const #21 = Method #1.#11; // Volatile."":()V
//const #22 = Method #1.#23; // Volatile.increase:()V
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: new #1; //class Volatile BY: create a new Volatile object, after: Stack (top)ex(bottom)
3: dup BY:after: Stack (top)ex,ex(bottom)
4: invokespecial #21; //Method "":()V BY:initialize ex, top popped for this instructino after: Stack (top)ex(bottom)
7: astore_1 BY: save ex to Slot #1 .after: Stack (top)ex(bottom)
8: aload_1 BY: save ex back to stack .after: Stack (top)ex(bottom). Note, no change for stack, after astore_1 and aload_1, but the Slot #1 saved value 'ex' why it is required? Because the following invokation will take Slot. The Slot should prepare Slot[0] = this, Slot[1..m] as arguments. (20150630)
9: invokevirtual #22; //Method increase:()V
12: return
LineNumberTable:
line 19: 0
line 20: 8
line 21: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 ex LVolatile;
}