深入学习理解JVM系列(五) -- 了解类文件结构
一、概述
- 一次编写,到处运行(Write Once, Run Anywhere)
- 与平台/语言无关:不同平台的Java虚拟机,都统一支持字节码(Byte Code)程序存储格式
- Java虚拟机不与语言绑定,只和Class文件(二进制文件格式)关联
- Class文件(强制性语法和结构约束)包括:Java虚拟机指令集、符号表、辅助信息
二、 Class类文件的结构
使用javap工具分析字节码:javap -verbose TestClass
- 任何一个Class文件都对应着唯一的一个类或接口的定义信息
但类或接口不一定需要定义在Class文件里(可以动态生成,送入类加载器中) - Class文件:一组以字节为基础单位的二进制流
- 各个数据项严格按照顺序排列在文件之中,数据项需要占用多个字节空间时,按照高位在前(Big-Endian)的方式,分割成若干个字节存储
- Class文件格式:类似于C语言结构体的伪结构,只有两种数据类型(无符号数、表)
- 无符号数:u1/u2/u4/u8,代表1/2/4/8个字节的无符号数,描述数字、索引引用、数量值、字符串值
- 表(_info结尾):多个无符号数或其他表作为数据项构成的复合数据类型,描述有层次关系的复合结构数据
- Class文件可以当作一张表
1. 魔数与Class文件的版本
- 魔数(Magic Number):开始4个字节(0xCAFEBABE),确定Class文件是否能被虚拟机接受
- 版本号(4个字节:高版本JDK能向下兼容低版本的Class文件
- 次版本号(Minor Version):一般固定为零
- 主版本号(Major Version):JDK1.1:45,逐次加一,JDK8:52(0x0034)
2. 常量池(constant_pool)
- Class文件占用空间最大的数据项之一,第一个表类型数据项
- 入口:常量池容量计数值(constant_pool_count):u2,计数从1开始,0表示不引用任何一个常量池项目,0x0016(22-1项常量)
- 存放:字面量(Literal)和符号引用(Symbolic References)
- 字面量:常量(文本字符串、final常量值等)
- 符号引用:
- 被模块导出或者开放的包(Package)
- 类和接口的全限定名(Fully Qualified Name)
- 字段的名称和描述符(Descriptor)
- 方法的名称和描述符
- 方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)
- 动态调用点和动态常量(Dynamically- Computed Call Site、Dynamically- Computed Constant)
- 当虚拟机做类加载时,将会从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中
- 常量池中每一项常量都是一个表,表结构的起始第一位:u1(标志位 tag)表示属于哪种常量类型
3. 访问标志(access_flags)
识别类或接口层次的访问信息:
- 是类还是接口
- 是否定义public类型
- 是否定义abstract类型
- 类是否声明为final