Class文件结构
class文件是一种8位字节的二进制流文件, 各个数据项按顺序紧密的从前向后排列, 相邻的项之间没有间隙, 这样可以使得class文件非常紧凑, 体积轻巧, 可以被JVM快速的加载至内存, 并且占据较少的内存空间。 我们的Java源文件, 在被编译之后, 每个类(或者接口)都单独占据一个class文件, 并且类中的所有信息都会在class文件中有相应的描述, 由于class文件很灵活, 它甚至比Java源文件有着更强的描述能力。
魔数,claa文件版本
魔术是CAFEBABE,class文件版本表示字节码版本,防止低版本JVM运行高版本代码。
常量池
由于常量池的数量是不固定的,所以在常量池入口需要放置一项u2(即2个字节)类型的数据,代表常量池容量计数值(constant-pool-count)(从1开始,将0表示不引用任何常量).常量池中主要存放两大类常量:字面量(Literal)和符号引用(Synbolic Reference)。常量池是class文件中占用空间最多的。
一个常量池项cp_info的数据结构如下
cp_info{
u1 tag;
...
}
例如
CONSTANT_Long_info{
u1 tag=5;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_utf8_info{
u1 tag=1;
u2 length;
u1 byte[length];
}
其中的tag就是下表中的标志。
类型 | 标志 | 描述 |
---|---|---|
CONSTANT_utf8_info | 1 | UTF-8编码的字符串(字面) |
CONSTANT_Integer_info | 3 | int整形字面量(字面) |
CONSTANT_Float_info | 4 | float浮点型字面量(字面) |
CONSTANT_Long_info | 5 | long长整型字面量(字面) |
CONSTANT_Double_info | 6 | double双精度浮点型字面量(字面) |
CONSTANT_Class_info | 7 | 类或接口的全限定名(引用) |
CONSTANT_String_info | 8 | String类型的常量对象(引用) |
CONSTANT_Fieldref_info | 9 | 类中的字段(引用) |
CONSTANT_Methodref_info | 10 | 类中的方法(引用) |
CONSTANT_InterfaceMethodref_info | 11 | 类所实现的接口方法(引用) |
CONSTANT_NameAndType_info | 12 | 字段和方法的名称和类型(引用) |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄(引用) |
CONSTANT_MothodType_info | 16 | 标志方法类型(引用) |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点(引用) |
上面所有标记为引用的,最终都要具体内容都要引用到一个标志为字面的cp_info来存储具体信息。
CONSTANT_String_info{
u1 tag = 8;
u2 String_index;//指向某个CONSTANT_utf8_info结构体
}
具体请参考https://blog.csdn.net/wangtaomtk/article/details/52267548
访问标志
只有两个字节。标志该类是否为abstract,final,public,是否为接口等等。
类索引
一个U2类型,指向CONSTANT_Class_info类型的常量,记录本类的全限定名。
父类索引
一个U2类型,指向CONSTANT_Class_info类型的常量,记录父类的全限定名。
接口索引集合
接下来不定长的字节表示该类实现的接口。和上面类似。
字段表集合
一个字段用一个字段表表示,字段的集合当然要用字段表集合表示了。
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
access_flags的作用和类的访问标志作用类似。
name_index是本字段的索引,指向常量池中的一个CONSTANT_NameAndType_info类型。
descriptor_index用于描述该字段的数据类型
attributes_count属性表长度(非必须)
attributes 属性表(非必须)
方法表集合
方法表的组成和字段表一摸一样。只不过,descriptor_index还需要包含字段的数据类型、参数列表、返回值。另外,属性表中有一个code项目,用来存储源代码。
如果本class没有重写父类的方法,java还没有傻到把父类的方法原封不懂的拷贝到子类的方法表中。
字节码
每个字节码命令都有自己的助记符。(和汇编语言相同)。助记符的第一个字母表示自己所操作的数据类型。字节码设计遵循公有设计和私有实现原则。所有JVM都遵循一致的字节码规范,但是内部实现各不相同
加载和存储指令
运算指令
类型转换指令
对象创建和访问指令
JVM中的数组是一个比较特殊的问题。所以创建数组,把元素存储到数组,取数组长度,都有自己的指令
控制转移指令
方法调用和返回指令
异常处理指令
同步指令
monitorenter和monitorexit。(将栈顶元素作为锁进行同步)