前言:
本篇主要讲解了JVM对class的文件结构的一个读取翻译过程,对class的文件结构的魔数、版本号、常量池计数器、常量池、常量池表、接口计数器、接口集合(表结构)、字段计数器、字段(表结构)、方法计数器、方法(表结构)、属性计数器、属性(表信息)的结构化分析。
1、jvm基础
jvm的编码过程
jvm是一种规范,所有可以编译成class的文件都可以执行;jvm和java无关,所有可以编译成class的语言都可以在jvm运行。
![](https://img-blog.csdnimg.cn/img_convert/51df149c82fd4b5e99614e83870aab69.png)
jvm的实现
jvm官方默认实现方式hotSpot java -version
![](https://img-blog.csdnimg.cn/img_convert/f0ff6756cfc640f2aae493945ac9641d.png)
JDK JRE JVM
JDK=JRE(java运行时环境)+开发工具
JRE=jvm(虚拟机)+core lib(核心类库)
JVM java虚拟机
![](https://img-blog.csdnimg.cn/img_convert/726d29249c03484e80c0552960688e9f.png)
2、class文件的分析
官方文档位置:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1
二进制字节码的结构
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];
}
class文件读取的是二进制文件流,对应的class十六进制文件查看
![](https://img-blog.csdnimg.cn/img_convert/a2346b1cb0b24367884253885f8eb9e6.png)
class文件十六进制的内容解析
![](https://img-blog.csdnimg.cn/img_convert/b81c168f77db469087a3aeed1c1baa30.png)
class文件的反汇编
java源码
packagecom.java.czing.jvm.classfile;
/** * @Description TODO
* @Author wangchengzhi * @Date 2023/2/17 15:36 */
publicclassClassTest {
publicClassTest() {
}
}
![](https://img-blog.csdnimg.cn/img_convert/15946ee9077241deb48b5614785b8a8b.png)
常量池(存放所有常量)
![](https://img-blog.csdnimg.cn/img_convert/0e7f7714113a4aebb43c4fec67d3c908.png)
class文件主要结构梳理
![](https://img-blog.csdnimg.cn/img_convert/a906c9599848434ba9102b8c8c6ca739.png)
魔数(4字节):Class文件的标志
前几位固定数值为CA FE BA BE标志该文件为class文件;
它的唯一作用是确定这个文件是否为一个能被虚拟机接受的有效合法的Class文件
Class文件版本号
紧挨着魔数的4字节是次版本号Minor Version和主版本号Major Version
(class文件的版本号例如:JDK1.7默认为51.0JDK1.8默认为52.0)
![](https://img-blog.csdnimg.cn/img_convert/7734a42d4d714a25810fa53cf4d9dc41.png)
常量池计数器:constant_pool_count
由于常量池的数量不固定,时长时短,所以需要放置两个字节来表示常量池容量计数值。
常量池容量计数值(u2类型):从1开始,表示常量池有多少项常量。即constant_pool_count=1表示常量池中有0个常量项。例如:常量池的数量 00 10(16进制) = 1*161+0*160=16,数量16-1=15。
![](https://img-blog.csdnimg.cn/img_convert/b281e1b71e00497bb6a8a14af7396b26.png)
常量池:constant_pool
常量池中每一项常量都是一个表
u1:一个字节。
u2:两个字节。
![](https://img-blog.csdnimg.cn/img_convert/6986913dd302469b9a86b9adea16857d.png)
访问标志:access_flag
在常量池后,紧跟着访问标记。该标记使用两个字节表示,用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。各种访问标记如下所示:
标志名称 | 标志值 | 含义 |
ACC_PUBLIC | 0x0001 | 标志位public类型 |
ACC_FINAL | 0x0010 | 标志被声明为final,只有类可以设置 |
ACC_SUPER | 0x0020 | 标志允许使用invokespecial字节码指令的新语义,JDK1.0.2之后编译出来的类的这个标志默认为真。(使用增强的方法调用父类方法) |
ACC_INTERFACE | 0x0200 | 标志这是一个接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假 |
ACC_SYNTHETIC | 0x1000 | 标志此类并非由用户代码产生(即:由编译器产生的类,没有源码对应) |
ACC_ANNOTATION | 0x2000 | 标志这是一个注解 |
类索引(this_class)、父类索引(super_class)、接口计数器(INTERFACE_COUNT)、接口索引集合(interfaces),均为两字节
在访问标记后,会指定该类的类别、父类类别以及实现的接口
字段表集合
Fields:
1.用于描述接口或类中声明的变量。字段(field)包括类级变量以及实例级变量,但是不包括方法内部、代码块内部声明的局部变量。
2.字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。
3.它指向常量池索引集合,它描述了每个字段的完整信息。比如字段的标识符、访问修饰符(public、private或protected)、是类变量还是实例变量(static修饰符)、是否是常量(final修饰符)等
fields_count(字段计数器):
1.fields_count的值表示当前class文件fields表的成员个数。使用两个字节来表示。
2.fields表中每个成员都是一个field_info结构,用于表示该类或接口所声明的所有类字段或者实例字段,不包括方法内部声明的变量,也不包括从父类或父接口继承的那些字段。
fields[](字段表):
1.fields表中的每个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描述。
2.一个字段的信息包括如下这些信息。这些信息中,各个修饰符都是布尔值,要么有,要么没有。
(1)作用域(public、private、protected)
(2)是实例变量还是类变量(static修饰符)
(3)可变性(final)
(4)并发可见性(volatile修饰符,是否强制从主内存读写)
(5)可否序列化(transient修饰符)
(6)字段数据类型(基本数据类型、对象、数组)
(7)字段名称
字段表结构:
类型 | 名称 | 含义 | 数量 |
U2 | access_flags | 访问标志 | 1 |
U2 | name_index | 字段名索引 | 1 |
U2 | descriptor_index | 描述符索引 | 1 |
U2 | attributes_count | 属性计数器 | 1 |
attribute_info | attributes | 属性集合 | attributes_count |
方法计数器(methods_count)
methods_count的值表示当前class文件methods表的成员个数。使用两个字节来表示。
方法表(methods)
1.methods表中的每个成员都必须是一个method_info结构,用于表示当前类或接口中某个方法的完整描述。如果某个method_info结构的access_flags项既没有设置ACC_NATIVE标志也没有设置ACC_ABSTRACT标志,那么该结构中也应包含实现这个方法所用的Java虚拟机指令。
2.method_info结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法和类或接口初始化方法
3、方法表的结构同字段表
属性计数器(attributes_count - u2)
attributes_count的值表示当前class文件属性表的成员个数。属性表中每一项都是一个attribute_info结构。
属性表(attributes)
属性表的每个项的值必须是attribute_info结构。属性表的结构比较灵活,各种不同的属性只要满足以下结构即可。
类型 | 名称 | 数量 | 含义 |
U2 | attribute_name_index | 1 | 属性名索引 |
U4 | attribute_length | 1 | 属性长度 |
U1 | info | attribute_length | 属性表 |
附件:JVM的官方文档参考https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.7
结语:
jvm的class文件结构化梳理,欢迎留言交流。