根据 j2se7 Virtual Machine Specific:http://docs.oracle.com/javase/specs/jvms/se7/html/
- 源文件:
public class Person {
private String name;
public void walk(){
System.out.println("I am walking.");
}
}
- 对应 16 进制表示:
![](https://img.haomeiwen.com/i26273155/e486eda871e4b183.png)
CA FE BA BE 00 00 00 33 00 21 07 00 02 01 00 06 50 65 72 73 6F 6E 07 00 04 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 04 6E 61 6D 65 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 0A 00 03 00 0B 0C 00 07 00 08 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00 08 4C 50 65 72 73 6F 6E 3B 01 00 04 77 61 6C 6B 09 00 12 00 14 07 00 13 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D 0C 00 15 00 16 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 08 00 18 01 00 0D 49 20 61 6D 20 77 61 6C 6B 69 6E 67 2E 0A 00 1A 00 1C 07 00 1B 01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 0C 00 1D 00 1E 01 00 07 70 72 69 6E 74 6C 6E 01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 0B 50 65 72 73 6F 6E 2E 6A 61 76 61 00 21 00 01 00 03 00 00 00 01 00 02 00 05 00 06 00 00 00 02 00 01 00 07 00 08 00 01 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7 00 0A B1 00 00 00 02 00 0C 00 00 00 06 00 01 00 00 00 02 00 0D 00 00 00 0C 00 01 00 00 00 05 00 0E 00 0F 00 00 00 01 00 10 00 08 00 01 00 09 00 00 00 37 00 02 00 01 00 00 00 09 B2 00 11 12 17 B6 00 19 B1 00 00 00 02 00 0C 00 00 00 0A 00 02 00 00 00 06 00 08 00 07 00 0D 00 00 00 0C 00 01 00 00 00 09 00 0E 00 0F 00 00 00 01 00 1F 00 00 00 02 00 20
- class 文件规范格式应为:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
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];
}
- 字节码分析:(u1 表示 1 个字节,u2 表示 2 个字节,依次类推...)
CA FE BA BE // 标识该文件为.class文件[u4];
00 00 00 33 // 标识jdk编译版本: 小版本[u2] 大版本[u2], 这里大版本为51, 说明该文件由jdk7编译而来;
00 21 // 标识该class文件的常量池个数[u2], 说明该文件有33个常量项
java7 规范中常量池中常量项的类型有 (常量项索引从 1 开始计数,记住每一种常量类型都有各自的结构):
Constant Type | Value |
---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
- 先分析常量池:
第 1 个常量:
07 00 02
07 // 07 表示该常量为CONSTANT_Class类型的常量,我们看看CONTSTANT_Class类型常量的结构:
CONSTANT_Class:{
u1: 标志(07)
u2: 常量池索引,上面这个例子 [00 02] 说明指向的就是第2个常量项,再继续看第2个常量项:即Person
}
第 2 个常量:
01 00 06 50 65 72 73 6F 6E
01 // 表示该常量为CONSTANT_Utf8类型的常量类型,CONSTANT_Utf8常量类型结构为:
CONSTANT_Uft8:{
u1: 标志(01)
u2: 字符串长度, [00 06]表示这个字符串占6个字节
u[int[u2]]: 表示第三部分的长度由u2值决定,这里是50 65 72 73 6F 6E, 这6个字节对应的ASCII码就是Person, 明白了吧
}
第 3 个常量:
07 00 04
07 //CONSTANT_Class常量类型,且其字符串指向第4(00 04)个常量,即java/lang/Object
第 4 个常量:
01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
01 //CONSTAT_Utf8常量类型,且长度为16[00 10], 紧跟的16个字节为6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74, 就是java/lang/Object
第 5 个常量:
01 00 04 6E 61 6D 65
01 //CONSTANT_Utf8
00 04 //4个字节长度
6E 61 6D 65 --> name
第 6 个常量:
01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B
01 //CONSTANT_Utf8
00 12 //18个字节长度
// 4C 6A ... 67 3B --> Ljava/lang/String;
第 7 个常量:
01 00 06 3C 69 6E 69 74 3E
01 //CONSTANT_Utf8
00 06 //6个字节长度
3C 69 6E 69 74 3E --> <init>
第 8 个常量:
01 00 03 28 29 56
01 //CONSTANT_Utf8
00 03 //3个字节
28 29 56 --> ()V
第 9 个常量:
01 00 04 43 6F 64 65
01 //CONSTANT_Utf8
00 04 //4个字节
43 6F 64 65 --> Code
第 10 个常量:
0A 00 03 00 0B
0A // CONSTANT_Methodref
CONSTANT_Methodref {
u1(11) 标志
u2 指向方法所属类的描述符CONSTANT_Utf8的索引项
u2 指向方法名称及类型描述符CONSTANT_NameAndType的索引项
}
00 03 //第3个常量:如前面的,为java/lang/Object这个超类
00 0B //第11个常量:根据第11个常量:为<init>方法
第 11 个常量:
0C 00 07 00 08
0C //CONSTANT_NameAndType
CONSTANT_NameAndType结构:
{
u1(12) 标志
u2 指向字段或方法名称的常量项索引
u2 指向字段或方法描述符的常量索引
}
00 07 //第7个常量:<init>
00 08 //第8个常量: ()V
就是指类实例的初始化方法
第 12 个常量:
01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 //CONSTANT_Utf8 15字节 LineNumberTable
第 13 个常量:
01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 //CONSTANT_Utf8 18字节 LocalVariableTable
第 14 个常量:
01 00 04 74 68 69 73 //CONSTANT_Utf8 4字节 this
第 15 个常量:
01 00 08 4C 50 65 72 73 6F 6E 3B //CONSTANT_Utf8 8字节 LPerson;
第 16 个常量:
01 00 04 77 61 6C 6B //CONSTANT_Utf8 4字节 walk
第 17 个常量:
09 00 12 00 14
09 //CONSTANT_Fieldref
CONSTANT_Fieldref结构:
{
u1(9) 标志
u2 指向字段所属类或接口描述符CONSTANT_Class的索引项
u2 指向字段描述符CONSTANT_NameAndType的索引项
}
00 12 //第18个常量:即java/lang/System
00 14 //第20个常量:out Ljava/io/PrintStream;
第 18 个常量:
07 00 13 //CONSTANT_Class 指向第19(00 13)个常量:即java/lang/System
第 19 个常量:
01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D //CONSTANT_Utf8 16个字节 java/lang/System
第 20 个常量:
0C 00 15 00 16 //CONSTANT_NameAndType 第21(00 15)个常量:out 第22(00 16)个常量:Ljava/io/PrintStream;
第 21 个常量:
01 00 03 6F 75 74 //CONSTANT_Utf8 3字节 out
第 22 个常量:
01 00 15 4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B //CONSTANT_Utf8 21字节 Ljava/io/PrintStream;
第 23 个常量:
08 00 18
08 //CONSTANT_String
CONSTANT_String常量类型结构:
{
u1(08) 标志
u2 指向字符串字面量的索引
}
00 18 //第24个常量:I am walking.
第 24 个常量:
01 00 0D 49 20 61 6D 20 77 61 6C 6B 69 6E 67 2E //CONSTANT_Utf8 13(00 0D)字节 I am walking.
第 25 个常量:
0A 00 1A 00 1C //CONSTANT_Methodref 第26(00 1A)个常量:java/lang/PrintStream 第28(00 1C)个常量:println (Ljava/lang/String;)V
第 26 个常量:
07 00 1B //CONSTANT_Class 第27(00 1B)个常量: java/lang/PrintStream
第 27 个常量:
01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D //CONSTANT_Utf8 19(00 13)字节 java/lang/PrintStream
第 28 个常量:
0C 00 1D 00 1E //CONSTANT_NameAndType 第29(1D)个常量: println 第30(00 1E)个常量: (Ljava/lang/String;)V
第 29 个常量:
01 00 07 70 72 69 6E 74 6C 6E //CONSTANT_Utf8 7字节 println
第 30 个常量:
01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 //CONSTANT_Utf8 21(00 15)字节 (Ljava/lang/String;)V
第 31 个常量:
01 00 0A 53 6F 75 72 63 65 46 69 6C 65 //CONSTANT_Utf8 10(00 0A)字节 SourceFile
第 32 个常量:
01 00 0B 50 65 72 73 6F 6E 2E 6A 61 76 61 //CONSTANT_Utf8 11(00 0B)字节 Person.java
第 33 个常量:null
终于结束常量池的解析,进入到其他内容解析:
- access_flags 表示该类或接口的访问属性
访问属性有:
ACC_PUBLIC | 0x0001 | Declared public; may be accessed from outside its package. |
---|---|---|
ACC_FINAL | 0x0010 | Declared final; no subclasses allowed. |
ACC_SUPER | 0x0020 | Treat superclass methods specially when invoked by the invokespecial instruction. |
ACC_INTERFACE | 0x0200 | Is an interface, not a class. |
ACC_ABSTRACT | 0x0400 | Declared abstract; must not be instantiated. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |
ACC_ENUM | 0x4000 | Declared as an enum type. |
00 21 // 即ACC_PUBLIC | ACC_SUPER, 确实符合Person类的访问属性,ACC_SUPER指是否允许使用invokespecial指令, jdk1.2后编译出的class文件的该标志均为true
- this_class
00 01 //指向CONSTANT_Class常量类型的索引,即第1个常量:Person
- super_class
00 03 //指向CONSTANT_Class常量类型的索引,即第3个常量:java/lang/Object
开始解析接口表:
- interfaces_count
00 00 //0, 确实我们的Person没有实现接口
- interfaces (无,因为上面 interfaces_count 为 0,如果有接口,则该属性为 [u2 u2 ..], 表示各接口对应的常量池中的索引项,且类型为 CONSTANT_Class)
接口表解析完成。
开始解析字段表:
- fields_count
00 01 //只有1个字段
- field_info, 这是属性域结构,其结构为:
{
u2: access_flags //访问标志
u2: name_index //对应字段名称,指向常量池索引,类型为CONSTANT_Utf8
u2: descriptor_index //对应字段描述符,指向常量池索引, 类型为CONSTANT_Utf8
u2: atrributes_count //字段的属性个数
attribute_info //属性信息,也具有自己的结构,后面遇到再讲
}
字段的访问标志有这些:
Flag Name | Value | Interpretation |
---|---|---|
ACC_PUBLIC | 0x0001 | Declared public; may be accessed from outside its package. |
ACC_PRIVATE | 0x0002 | Declared private; usable only within the defining class. |
ACC_PROTECTED | 0x0004 | Declared protected; may be accessed within subclasses. |
ACC_STATIC | 0x0008 | Declared static. |
ACC_FINAL | 0x0010 | Declared final; never directly assigned to after object construction (JLS §17.5). |
ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached. |
ACC_TRANSIENT | 0x0080 | Declared transient; not written or read by a persistent object manager. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ENUM | 0x4000 | Declared as an element of an enum. |
00 02 // 对应ACC_PRIVATE 私有域
00 05 // 字段的名称,看上面第5个常量,即name
00 06 // 字段的描述符,看上面第6个常量,即Ljava/lang/String;
00 00 // 0个属性,即该字段没有额外属性,但是也得说下attribute的结构
//attribute的结构:
{
u2: attribute_name_index //属性名称,指向常量池索引,类型为CONSTANT_Utf8
u4: attribute_length //属性长度
u1: info //具体属性信息,长度就是attribute_length
}
// 如果有多个属性,就按其结构顺序排列下去,这里name字段没有属性,下面就是方法属性等的分析了
字段表解析完毕。
开始解析方法表:
- methods_count 方法个数
00 02 // 该类有2个方法
- method_info, 方法信息
{ //有得说method_info结构了,其实和field_info是一样的,只是access_flags取值范围不一样
u2: access_flags //访问标志
u2: name_index //对应方法名称,指向常量池索引,类型为CONSTANT_Utf8
u2: descriptor_index //对应方法描述符,指向常量池索引, 类型为CONSTANT_Utf8
u2: atrributes_count //方法的属性个数
attribute_info //属性信息,同上
}
方法的访问标志表:
Flag Name | Value | Interpretation |
---|---|---|
ACC_PUBLIC | 0x0001 | Declared public; may be accessed from outside its package. |
ACC_PRIVATE | 0x0002 | Declared private; accessible only within the defining class. |
ACC_PROTECTED | 0x0004 | Declared protected; may be accessed within subclasses. |
ACC_STATIC | 0x0008 | Declared static. |
ACC_FINAL | 0x0010 | Declared final; must not be overridden (§5.4.5). |
ACC_SYNCHRONIZED | 0x0020 | Declared synchronized; invocation is wrapped by a monitor use. |
ACC_BRIDGE | 0x0040 | A bridge method, generated by the compiler. |
ACC_VARARGS | 0x0080 | Declared with variable number of arguments. |
ACC_NATIVE | 0x0100 | Declared native; implemented in a language other than Java. |
ACC_ABSTRACT | 0x0400 | Declared abstract; no implementation is provided. |
ACC_STRICT | 0x0800 | Declared strictfp; floating-point mode is FP-strict. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
这里讲一下属性的种类,后面分析得比较多:
Attribute | Section | Java SE | class file |
---|---|---|---|
ConstantValue | §4.7.2 | 1.0.2 | 45.3 |
Code | §4.7.3 | 1.0.2 | 45.3 |
StackMapTable | §4.7.4 | 6 | 50.0 |
Exceptions | §4.7.5 | 1.0.2 | 45.3 |
InnerClasses | §4.7.6 | 1.1 | 45.3 |
EnclosingMethod | §4.7.7 | 5.0 | 49.0 |
Synthetic | §4.7.8 | 1.1 | 45.3 |
Signature | §4.7.9 | 5.0 | 49.0 |
SourceFile | §4.7.10 | 1.0.2 | 45.3 |
SourceDebugExtension | §4.7.11 | 5.0 | 49.0 |
LineNumberTable | §4.7.12 | 1.0.2 | 45.3 |
LocalVariableTable | §4.7.13 | 1.0.2 | 45.3 |
LocalVariableTypeTable | §4.7.14 | 5.0 | 49.0 |
Deprecated | §4.7.15 | 1.1 | 45.3 |
RuntimeVisibleAnnotations | §4.7.16 | 5.0 | 49.0 |
RuntimeInvisibleAnnotations | §4.7.17 | 5.0 | 49.0 |
RuntimeVisibleParameterAnnotations | §4.7.18 | 5.0 | 49.0 |
RuntimeInvisibleParameterAnnotations | §4.7.19 | 5.0 | 49.0 |
AnnotationDefault | §4.7.20 | 5.0 | 49.0 |
BootstrapMethods | §4.7.21 | 7 | 51.0 |
第 1 个方法解析 <init>:
00 01 // 对应ACC_PUBLIC, 即该方法为public
00 07 // 方法名称,看第7个常量,即<init>方法,构造器方法
00 08 // 方法描述符,看第8个常量,即()V
00 01 // <init>方法有1个属性
00 09 // 该属性名称是第9个常量,即Code, 当然我们每个方法确实要有Code才行,哪怕是空的
Code是属性的一种,它也有自己的结构:
{
u2 attribute_name_index; //指向常量池索引,但其实字节码中并没有其值,固定为Code
u4 attribute_length;
u2 max_stack; //栈最大深度
u2 max_locals; //方法局部变量使用的最大空间,单位为Slot, 每个Slot可存放32位的数据类型,除long,double占2个Slot外,其余基本类型都占1个Slot
u4 code_length; //方法字节码指令长度,1个指令占1个字节,因为jvm至今只有200来个字节码指令,1个字节可表示256种指令
u1 code[code_length]; //对应的字节码指令序列,jvm栈是基于栈的指令操作,大多数指令都是单字节指令,但是也有多字节指令,如[invokespecial u2],其后面2个字节指向常量池索引的CONSTANT_Methodref类型的常量
u2 exception_table_length; //异常表长度,下面是异常表结构
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length]; //各个异常
u2 attributes_count; //属性个数
attribute_info attributes[attributes_count]; //各属性
}
00 00 00 2F attribute_length为37,之所以没有attribute_name_index,其实这里已经固定为Code了,没必要再占2个字节去指向常量池
00 01 //栈最大深度为1
00 01 //局部变量最大空间为1Slot
00 00 00 05 // 指令集长度为5
2A B7 00 0A B1 查看字节码指令集可知: 2A(aload_0,将第一个引用类型(this)本地变量推送至栈顶) B7(invokespecial, 调用超类构造方法,实例初始化方法或私有方法,后2个字节00 0A指向常量池索引,即java/lang/Object.<init>, B1(return))
00 00 // 异常表为0,所以下面就没有异常信息了,直接就attributes_count了
00 02 // 2个属性
解析第 1 个属性:
00 0C //指向第12个常量,即LineNumberTable, LineNumberTable也有自己的结构:
// LineNumberTable结构
{
u2 attribute_name_index; //同Code属性一致,固定为LineNumberTable, 所以字节码里也没有为其保留位置
u4 attribute_length;
u2 line_number_table_length; //下面是行号表结构
{ u2 start_pc; // 字节码行号(偏移量)
u2 line_number; // 源码行号
} line_number_table[line_number_table_length];
}
00 00 00 06 // attribute_length属性长度为6, 记住没有attribute_name_index的字节码,否则是多余
00 01 // 有1个行号表
00 00 // 字节码行号:0
00 02 // 源码行号:2
再解析第 2 个属性:
00 0D // 看第13个常量,即LocalVariableTable局部表量表,LocalVariableTable也是一种属性,其结构为:
// 局部变量表结构:
{
u2 attribute_name_index; //同理不占字节,固定为LocalVariableTable
u4 attribute_length;
u2 local_variable_table_length; //局部变量与当前栈帧的关系结构
{ u2 start_pc; // 局部变量起始偏移量
u2 length; //局部变量作用范围长度
u2 name_index; //局部变量名称,指向常量池索引
u2 descriptor_index; //局部变量描述符,指向常量池索引
u2 index; //所在局部变量数组的索引位置, 如果为long或double在index和index+1
} local_variable_table[local_variable_table_length];
}
00 00 00 0C attrbute_length为12
00 01 // 有一个局部变量与当前栈帧的关系
00 00 // 起始偏移0
00 05 // 作用范围长度为5
00 0E // 名称指向14个常量,即this
00 0F // 描述指向第15个常量,即LPerson;
00 00 // 即为局部变量数组的第0个
解析第 2 个方法 walk:
00 01 //访问属性为public
00 10 //方法名称,第16个常量,即walk
00 08 //方法描述符,第8个常量,即()V
00 01 //属性个数为1
解析该属性:
00 09 //指向第9个常量,即Code
00 00 00 37 //attribute_length为45
00 02 //最大的栈深度为2
00 01 //最大局部变量Slot数为1
00 00 00 09 //代码长度9
B2 00 11 //B2:getstatic 00 11:第17个常量,即System.out的引用放到栈顶
12 17 //12:ldc 讲第23个常量(int, float, 或者String字面量的引用)放到栈顶,即将I am walking.的引用放到栈顶
B6 00 19 //B6:invokevirtual调用实例方法,这里就是println
B1 //B1:return方法返回
00 00 //没有异常表
00 02 //有2个属性
第 1 个属性 LineNumberTable:
00 0C // 第12个常量,即LineNumberTable
00 00 00 0A //属性长度为10
00 02 //有2个行号表
00 00 00 06 00 08 00 07 //2个行号表分别为:0:6 8:7
第 2 个属性 LocalVariableTable:
00 0D //属性类型为LocalVariableTable
00 00 00 0C //属性长度为12
00 01 //有1个局部变量
00 00 //该变量偏移量为0
00 09 //该变量作用范围长度为9
00 0E //该变量名称指向第14个常量,即this
00 0F //该变量描述符指向第15个常量 ,即LPerson;
00 00 //该变量在局部变量数组的第0个位置
到此方法表解析完毕。
下面就是类的属性表了:
00 01 //有1属性
00 1F //属性类型指向第31个常量,即SourceFile
{//SourceFile结构
u2 attribute_name_index; //固定为SourceFile
u4 attribute_length; //属性长度
u2 sourcefile_index; //源文件名称,指向常量池索引
}
00 00 00 02 //属性长度为2
00 20 //源文件名称指向第32个常量,即Person.java
类的属性表解析完毕。
网友评论