JAVA源文件编译之后形成class文件,也就是java字节码文件,然后java虚拟机解释执行
java字节码文件。我们来简单看看java字节码文件的格式。下图是java字节码文件的格式的定义。
u4代表占4个字节,cp_info表示cp_info结构需要占据的字节,后面会介绍cp_info结构。
这些count的最大长度意味着为0xFFFF=65535,如果超过,javac会造成“code too large”错误
假设有一个java文件的源代码如下
public class ClassFormatTest {
public static String staticStr = "static";
private int instanceInt = 1;
private int[] intArray = { 1, 2, 3 };
static{
staticStr="changed in static block";
}
public int add(int c){
int a = 10;
int b = 23;
int d = a + b;
return d;
}
}
我们来看看编译成二进制文件后文件的内容,以16进制的形式打开,我们可以看到如下图所示:
ca fe ba be 为magic number,00 00 00 32 为此版本号和主版本号 0x32=50=jdk1.7
接下来是常量池的数量00 27=39,对应constant_pool_count,那么一共有39-1=38个常量项。
常量池是数组的形式。保存了类中用到的一些不变的"常量",如标识符public,private,
类名称,方法名,数据类型,字面值,变量名称等等。
接下来是第一个常量项,第一个字节07 是这个常量项的类型。
常量池中的16进制和类型的对应如下图所示:
根据常量池中的16进制和类型的对应关系图可以得知这是一个class的信息。
一个class的cp-info结构如下图所示:
00 02表示它的值为常量池数组的第二项。为const #2 = Asciz ClassFormatTest;(参照后面javap反编译的结果)
字节码的项于项之间是没有空格的,接下来第二项01表示常量项type为utf8编码的字符串,
对应下图的utf8 string的cp-info结构,可以得知 00 0f是表示长度为15.
如上面二进制文件那副图中划线的内容,后面的15个字节的内容表示的字符串值为ClassFormatTest。
常量池一致持续到划椭圆的代表这个类的访问标志符 0x0021(public,jkd1.2之后)
16进制数和访问标识符对应关系如下图所示:
访问标识符(access flags)之后的00 01 00 03 00 00分别表示this class,supper class,
interfaces count,由于interfaces count是 0000所以interfaces没有字节表示。
接下来是fields的信息,如下图所示所示的0003这两个字节表示fields_count:
00 03表示fields 数量为3。
接下来是第一个字段的信息,参照下图中的字段结构对应关系,
接下来两个字节为access flag, 0x0009表示 public static,
再接下来两个字节为字段名,00 05指向常量池中的第5项,const #5 = Asciz staticStr;(参照后面javap反编译的结果)
再接下来两个字节为字段类型,00 06指向常量池中的第6项,const #6 = Asciz Ljava/lang/String;;
再接下来两个字节为字段属性个数,00 00表示没有attributes。
接下就是第二个字段的信息
0002为第二个field的access flag信息,表示 private。
00 07指向常量池中的第7项,const #7 = Asciz instanceInt;
00 08指向常量池中的第8项,const #8 = Asciz I;
00 00表示没有attributes。
其他字段类推...
字段结构对应关系如下图
methods_count字节位置的0003表示有3个方法,
接下来两个字节是第一个方法的访问标识符,0008表示为static
接下来两个字节为方法名,000b对应 const #11 = Asciz <clinit>; (类构造器方法)
再接下来两个字节为方法描述,000c对应 const #12 = Asciz ()V;
再接下来两个字节为属性个数,00 01表示有一个属性。
再接下来两个字节为属性值, 00 0d表示常连池的第13项const #13 = Asciz Code;
下面是方法的结构关系图
接下来是方法代码的信息,根据下图code结构的对应关系分析:
我们来仔细分析一下add方法
划线的00 01 00 1f 00 20 00 01 00 0d中的
00 01是方法的访问标识符表示这是 public。
00 1f是方法的的名称add方法(const #31),
00 20表示descriptor为const #32 = Asciz (I)I; 参数int,返回值为int。
00 01表示有一个属性,00 0d表示根据属性的index参照常量池中得#13项为代码。
接下来的4字节00 00 00 6c 表示属性的长度为 108。(这个长度包括code字节码,
exception,LineNumberTable和LocalVariableTable等)
在接下来的00 02 表示Stack=2,
00 05表示Locals=5,
接下来的4字节00 00 00 0e 表示code的长度为14。
接下来的14个字节为虚拟机的字节码指令,10 0a 3d 10 17 3e 1c 1d 60 36 04 15 04 ac
10 0a对应 bipush 10,3d 对应istore_2,10 17对应bipush 23,
3e对应istore_3,直到ac对应ireturn
两个字节合起来的话,后面的一个是操作数,有的指令是不需要操作数的则为一个字节。
接下来的0000表示exception_table_length为0.
再接下来的0002表示attributes_count为2.
再接下来的0014对应到常量池中的第20项,const #20 = Asciz LineNumberTable;表示为LineNumberTable.
接下来为line_number_table_length,0004表示有4个行对应关系。
00 00 00 0b为一组对应,00 03 00 0c为一组对应,
00 06 00 0d为一组对应,00 0b 00 0e为一组对应。
接下来的0000 000b表示这个方法字节code第0个(也就是第一个)字节码指令与JAVA源码的11行对应。
最多两个字节表示行号,则java的方法的代码行数是有限制的。LineNumberTable的结构如下
另外一个attribute为LocalVariableTable,从0015开始,共有5个local变量。
根据如下的结构图可以分析:
这5个local变量为this,c,a,b和d。
以变量a为例字节码为 00 03 00 0b 00 22 00 08 00 02,
00 03 表示的信息为局部变量开始有效时字节码的偏移位置。
00 0b 表示的信息为作用范围覆盖的长度。上面两者结合判断作用范围。
00 22 表示的信息为名称,常量池中第34项对应的值,为const #34 = Asciz a;
00 08 表示的信息为类型,常量池中第8项对应的值,const #8 = Asciz I;
00 02 表示的信息为在栈中保存的位置。
再接下来的0025表示attributes在常量池中的索引号,对应const #37 = Asciz SourceFile;
表示为SourceFile属性,根据下面的SourceFile结构关系图可以得知
对应到源文件名为const #38 = Asciz ClassFormatTest.java;
通过javap命令得到的内容如下:[javap -verbose -c ClassFormatTest > javap.txt]
Compiled from "ClassFormatTest.java"
public class ClassFormatTest extends java.lang.Object
SourceFile: "ClassFormatTest.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #2; // ClassFormatTest
const #2 = Asciz ClassFormatTest;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz staticStr;
const #6 = Asciz Ljava/lang/String;;
const #7 = Asciz instanceInt;
const #8 = Asciz I;
const #9 = Asciz intArray;
const #10 = Asciz [I;
const #11 = Asciz <clinit>;
const #12 = Asciz ()V;
const #13 = Asciz Code;
const #14 = String #15; // static
const #15 = Asciz static;
const #16 = Field #1.#17; // ClassFormatTest.staticStr:Ljava/lang/String;
const #17 = NameAndType #5:#6;// staticStr:Ljava/lang/String;
const #18 = String #19; // changed in static block
const #19 = Asciz changed in static block;
const #20 = Asciz LineNumberTable;
const #21 = Asciz LocalVariableTable;
const #22 = Asciz <init>;
const #23 = Method #3.#24; // java/lang/Object."<init>":()V
const #24 = NameAndType #22:#12;// "<init>":()V
const #25 = Field #1.#26; // ClassFormatTest.instanceInt:I
const #26 = NameAndType #7:#8;// instanceInt:I
const #27 = Field #1.#28; // ClassFormatTest.intArray:[I
const #28 = NameAndType #9:#10;// intArray:[I
const #29 = Asciz this;
const #30 = Asciz LClassFormatTest;;
const #31 = Asciz add;
const #32 = Asciz (I)I;
const #33 = Asciz c;
const #34 = Asciz a;
const #35 = Asciz b;
const #36 = Asciz d;
const #37 = Asciz SourceFile;
const #38 = Asciz ClassFormatTest.java;
{
public static java.lang.String staticStr;
static {};
Code:
Stack=1, Locals=0, Args_size=0
0: ldc #14; //String static
2: putstatic #16; //Field staticStr:Ljava/lang/String;
5: ldc #18; //String changed in static block
7: putstatic #16; //Field staticStr:Ljava/lang/String;
10: return
LineNumberTable:
line 4: 0
line 8: 5
line 2: 10
public ClassFormatTest();
Code:
Stack=5, Locals=1, Args_size=1
0: aload_0
1: invokespecial #23; //Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #25; //Field instanceInt:I
9: aload_0
10: iconst_3
11: newarray int
13: dup
14: iconst_0
15: iconst_1
16: iastore
17: dup
18: iconst_1
19: iconst_2
20: iastore
21: dup
22: iconst_2
23: iconst_3
24: iastore
25: putfield #27; //Field intArray:[I
28: return
LineNumberTable:
line 2: 0
line 5: 4
line 6: 9
line 2: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 this LClassFormatTest;
public int add(int);
Code:
Stack=2, Locals=5, Args_size=2
0: bipush 10
2: istore_2
3: bipush 23
5: istore_3
6: iload_2
7: iload_3
8: iadd
9: istore 4
11: iload 4
13: ireturn
LineNumberTable:
line 11: 0
line 12: 3
line 13: 6
line 14: 11
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LClassFormatTest;
0 14 1 c I
3 11 2 a I
6 8 3 b I
11 3 4 d I
}