1、class文件的结构
字节码文件的结构包括:魔数、版本号、常量池、访问标志、类索引以及父类索引或者接口索引信息(集合)、字段(类变量和成员变量)信息(集合)、方法信息(集合)
java class文件的整体结构如下:
名称 | 释义 | 大小 |
---|---|---|
magic | 魔数 | 4 Byte |
minor_version | 子版本号 | 2Byte |
major_version | 主板本号 | 2Byte |
constant_pool_count | 常量池的长度 | 2Byte |
constant_pool | 常量池的内容 | 有constant_pool_count决定 |
access_flags | 访问标志位 | 2Byte |
this_class | 类索引 | 2Byte |
super_class | 父类索引 | 2Byte |
interfaces_count | 实现接口的数目 | 2Byte |
interfaces | 实现的接口 | 由interfaces_count决定 |
fields_count | 成员变量的数目 | 2Byte |
fields | 成员变量 | 由fields_count决定 |
methods_count | 方法数 | 2Byte |
methods | 方法 | 由methods_count决定 |
attributes_count | 属性数 | 2Bytes |
attributes | 属性 | 由attributes_count决定 |
对于一端java代码:
public class MyClassTest {
private int a = 1;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
使用 javap -verbose MyClassTest.class解析出二进制字节码
F:\workspace\JvmTest\bin\xcs\com\cn>javap -verbose MyClassTest.class
Classfile /F:/workspace/JvmTest/bin/xcs/com/cn/MyClassTest.class
Last modified 2019-8-29; size 483 bytes
MD5 checksum e6455c493674f12e813ea5c766e419fe
Compiled from "MyClassTest.java"
public class xcs.com.cn.MyClassTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // xcs/com/cn/MyClassTest
#2 = Utf8 xcs/com/cn/MyClassTest
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Methodref #3.#11 // java/lang/Object."<init>":()V
#11 = NameAndType #7:#8 // "<init>":()V
#12 = Fieldref #1.#13 // xcs/com/cn/MyClassTest.a:I
#13 = NameAndType #5:#6 // a:I
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lxcs/com/cn/MyClassTest;
#18 = Utf8 getA
#19 = Utf8 ()I
#20 = Utf8 setA
#21 = Utf8 (I)V
#22 = Utf8 SourceFile
#23 = Utf8 MyClassTest.java
{
public xcs.com.cn.MyClassTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>
":()V
4: aload_0
5: iconst_1
6: putfield #12 // Field a:I
9: return
LineNumberTable:
line 3: 0
line 4: 4
line 3: 9
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lxcs/com/cn/MyClassTest;
public int getA();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #12 // Field a:I
4: ireturn
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lxcs/com/cn/MyClassTest;
public void setA(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #12 // Field a:I
5: return
LineNumberTable:
line 10: 0
line 11: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lxcs/com/cn/MyClassTest;
0 6 1 a I
}
SourceFile: "MyClassTest.java"
1)魔数:4个字节,java的标志,值为CAFEBABE
2)版本号:紧跟魔数之后的4个字节,前两个字节是minor version(次版本号),后两个字节是major version(主版本号)。
3)常量池(constant pool):紧接着主版本号之后就是常量池入口,是class文件的资源仓库,java类中的很多信息都是由常量池来维护和描述的,常量池是class文件中最大的数据项目之一。
----常量池开始的两个字节表示常量池的项目数,一般从1开始计数;比如前两个字节 00 16表示长度是22,但是只有21项常量,因为从1开始计数,0索引通常表示"不引用任何一个常量池项",但是对于字段、方法集合索引都是从0开始。
----常量池中主要存放两大类:
------字面量和符合引用。字面量比如文本字符串、声明为final的常量值等。
------而符号引用包括:
类和接口的全限定名
字段的名称和描述符(描述符是指访问控制符、类型等信息)
方法的名称和描述符(指方法的参数个数、类型、顺序、返回值等)。
------常量池的每一项常量都是一张表,标的结构不固定,但是表的第一项内容是u1(一个字节)类型的,通常表示字符串、整形、浮点型等。
常量池的常量项表有14个,如下图:
常量池每一张表的结构:
每个表的结构:
第一个tag(u1)表示是什么类型的表,比如3代表整形表;
第二个name_index(u2)表示,如果是基本类型,表示该基本类型变量的值所在的class中的位置的索引。如果是引用类型,比如String,第二个index代表String的长度,
第三个是String的值,使用UTF-8缩略码表示。
注:UTF-8缩略码与普通UTF-8编码的区别
UTF-8缩略码:从‘\u0001’到‘\u007f’之间的字符(相当于1-127的ASCII码)的缩略码使用一个字节表示;从’\u0080’到’\u07ff’之间的所有字符的缩略码使用两个字节表示;从’\u0800’到’\uffff’之间的所有字符的缩略码就是普通UTF-8的规则使用3个字节表示。
4)访问标志:常量池之后的两个字节,表示该类或者接口的访问标志public ,protected,如果是类是否被声明为final。如果一个类被public和final共同修饰,则使用这两个值的并集表示,比如public是0X0001,final是0X0010,则这个类的访问修饰符对应的字节码值是0X0011。
访问修饰符标志表如下:
5)类索引以及父类索引或者接口索引信息(集合):在访问标志位后面紧接着的2个字节表示当前类的全限定名的索引值,再接着2个字节表示父类的全限定名的索引值;接下来接口由多个2字节表示,第一个2字节表示有多少个接口,比如是3表示有个接口;接下来的3个2字节分别大鸟3个接口的全限定名索引值。
注意:给出的都是索引值,根据索引可以在常量池中找到他们对应的真是字符串的值
6)字段表:在类或接口信息之后,就是字段表(类变量或者成员变量,不包括方法中的局部变量)。
字段表结构如下图:
其中名字索引对应常量池中的字段名称引用,描述符索引对应字段的类型,是基本类型还是引用类型还是void(对应方法)。
7)方法表:在字段表之后的两个字节表示有多少个方法,之后就是每个方法的结构。一个方法表中包括:访问标志符、方法名索引、描述索引(参数、返回值等)、属性长度、属性表(主要包括最大栈深度、本地变量表、以及Code,code就是方法中的具体代码编译成的字节码指令,在执行方法时被JVM执行的指令)
8)属性表:属性表示方法表或者字段表里面的一部分,不是单独存在的。比如方法表中的Code、字段final关键字的常量值都属于属性表的内容。以及异常,存在于方法表,也是属性表的范畴。