写在前面的话
class文件结构可以说是一套规范,不一定是Java编译器编译出来的版本,它用来描述一个文件中对应的字段,类,常量池,继承关系等等。值得一提的是这个文件是完全紧凑型的。从第一个字节到最后一个字节,表达的意思按照约定好的严格规范。由于这种字节码可以运行在jvm上实现跨平台的亮点。所以不只是Java语言编译器可以编译成这种文件。class文件是学习jvm中比较重要的一环。我尽量用案例来分析一个class文件的解析。还是少一些理论性的东西.
案例源码
package structureclass.demo;
public class TestClass{
private int m;
public int inc(){
return m+1;
}
}
class字节码(winhex截图)
字节码部分构成
字节数与类型 | 名称 | 数量 |
4个 | magic(魔数) | 1 |
2个 | minor_version(次版本号) | 1 |
2个 | major_version(主版本号) | 1 |
2个 | constant_pool_count(常量池数量,从1开始) | 1 |
cp_info | constant_pool(常量池) | constant_pool_count - 1 |
2个 | constant_pool_count(常量池数量,从1开始) | 1 |
2个 | access_flags(访问标志) | 1 |
2个 | this_class(类索引) | 1 |
2个 | super_class(父类索引) | 1 |
2个 | interfaces_count(接口计数器) | 1 |
2个 | interfaces(接口描述) | interfaces_count |
2个 | fields_count(字段表集合count) | 1 |
field_info | fields(字段表集合) | fields_count |
2个 | methods_count(方法表集合count) | 1 |
method_info | methods(方法表集合) | methods_count |
2个 | attributes_count(属性表集合count) | 1 |
attribute_info | attributes(属性表集合) | attributes_count |
class字节码文件是严格按照这个顺序占位排列的
其中_info后缀叫做为表,我们把它理解成一个数据结构即可.
magic魔数
对应位置:0+4
如图所示,标识这个文件是一个class文件,这个是严格约束的。必须是这个文件才会被识别。可以理解为后缀与魔数的唯一绑定关系。对应结果CAFE BABE
minor_version&&major_version(次版本号和主版本号)
对应位置:0+4+4
虚拟机会向下兼容其版本号,对于高于其对应版本号的文件,直接拒绝,无论格式是否正确。对应结果:JDK1.7.0。此阶段不做过多研究,安装好jdk,随意编译一下即可知道对应的版本号
constant_pool_count(常量池数量)
对应位置:0+4+4+2
constant_pool_count从1开始,所以表示的数要计算结果以后-1,
0表示不引用任何常量
16进制33等于 3*16^0+1*16^1 = 19 -1 = 18
javap -verbose 输出:
λ javap -verbose TestClass.class
Classfile /E:/jvmana/TestClass.class
Last modified 2018-5-18; size 295 bytes
MD5 checksum 4ee039bbc694ca6a1383cb5bebb97d19
Compiled from "TestClass.java"
public class structureclass.demo.TestClass
SourceFile: "TestClass.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#16 // structureclass/demo/TestClass.m:I
#3 = Class #17 // structureclass/demo/TestClass
#4 = Class #18 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 inc
#12 = Utf8 ()I
#13 = Utf8 SourceFile
#14 = Utf8 TestClass.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = NameAndType #5:#6 // m:I
#17 = Utf8 structureclass/demo/TestClass
#18 = Utf8 java/lang/Object
{
public structureclass.demo.TestClass();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public int inc();
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
line 8: 0
}
我们可以看到正好是18项常量。与我们的计算结果一致。
那么,我们继续往下看
constant_pool(静态常量池)
未完待续