jvm学习笔记3:class类文件结构

1. 简介

Class文件是一组以8字节为基础单位的二进制流,
各个数据项目严格按照顺序紧凑排列在class文件中,
中间没有任何分隔符,这使得class文件中存储的内容几乎是全部程序运行的程序。

1.1 无符号数

属于基本数据类型,主要可以用来描述数字、索引符号、数量值或者按照UTF-8编码构成的字符串值,大小使用u1、u2、u4、u8分别表示1字节、2字节、4字节和8字节。

1.2 表

是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有的表都习惯以“_info”结尾。表主要用于描述有层次关系的复合结构的数据,比如方法、字段。需要注意的是class文件是没有分隔符的,所以每个的二进制数据类型都是严格定义的。具体的顺序定义如下:

类型名称数量备注
u4magic1魔数
u2minor_version1次版本号
u2major_version1主版本号
u2constant_pool_count1常量池计数器
c_infoconstant_poolconstant_pool_count -1常量池表
u2access_flags1
u2this_class1类索引
u2super_class1父类索引
u2interface_count1接口数量
u2interfacesinterface_count接口索引集合
u2field_count1字段数量
field_infofieldsfield_count字段表
u2method_count1字段数量
method_infomethodsmethod_count方法表
u2attributes_count1属性计数器
attribute_infoattributesattributes_count属性表

2.各部分详细介绍

2.1 魔数 magic

唯一的作用是确定这个文件是否为一个能被虚拟机所接受的class文件。值为:0xCAFEBABE

2.2 版本号 minor_version and major_version

用来标识版本号,JDK版本号只能向下兼容。

JDK版本class文件主版本号
JDK1.145
JDK1.246
JDK1.347
JDK1.448
JDK1.549
JDK1.650
JDK1.751
JDK1.842

2.3 常量池计数器和常量池表 constant_pool_count and constant_pool

常量池主要两大类常量:字面量和符号引用。字面量如文本字符串,什么为final的常量值;符号引用包括:类和类的全限定名、字段的名称和描述符、方法的名称和描述符。

常量池的每一项都是一个表,一共11(JDK1.7之前)+3(JDK1.7) = 14 种。每个常量开始的第一个字节称为标志位(tag),表示当前常量的类型。具体如下:

类型标志位描述项目组成
CONSTANT_Utf8_info1UTF-8编码的字符串u2 长度
u1 对应长度的字符串
CONSTANT _Interger_info3整型字面量u4 按高位在前存储int值
CONSTANT _Float_info4浮点型字面量u4 按高位在前存储float值
CONSTANT _Long_info5长整型字面量u8 按高位在前存储long值
CONSTANT _Double_info6双精度浮点型面量u8 按高位在前存储double值
CONSTANT _Class_info7类或接口的符号应用u2 指向全限定名常量项CONSTANT_Utf8_info的索引
CONSTANT_String_info8字符串类型字面量u2 指向字符串字面量CONSTANT_Utf8_info的索引
CONSTANT_Fieldref_info9字段的符号引用u2 指向字段所属类或接口CONSTANT_Class_info常量的索引)
u2 指向字段描述符_NameAndType_info 的索引
CONSTANT_Methodref_info10类中方法的符号引用u2 指向方法所属类CONSTANT_Class_info常量的索引)
u2 指向名称及类型描述符_NameAndType_info 的索引
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用u2 指向方法所属接口CONSTANT _Class_info常量的索引)
u2 指向名称及类型描述符_NameAndType_info 的索引
CONSTANT_NameAndType_info12字段或方法的部分符号引用u2 指向该字段或方法名称常量项的索引
u2 指向该字段或方法描述符常量项的引用
CONSTANT_MethodHandle_info15表示方法句柄
CONSTANT_MethodType_info3标识方法类型
CONSTANT_InvokeDynamic_info3表示一个动态方法调用点

2.4 访问标志 access_flags

u2:用来识别一些类或者接口的访问信息,主要包括以下内容:

  • 这个class是类 还是接口
  • 是否定义为public
  • 是否定义为abstract
  • 如果是类的话,是否被声明为final
标志名称标志值含义
ACC_PUBLIC0x0001是否为public
ACC_FINAL0x0010是否声明为final,只有类可设置
ACC_SUPER0x0020是否允许使用invokespecial字节码指令的新语义,这个指令的语义在jdk1.0.2发生过变化。为了区别,JDK1.0.2之后编译出来的类的这个标志必须为真
ACC_INTERFACE0x0200标志为接口
ACC_ABSTRACT0x0400是否为abtract类型,抽象类和接口为真
ACC_SYNTHETIC0x1000标志这个类并非由用户代码产生
ACC_ANNOTATION0x2000注解
ACC_ENUM0x4000枚举

2.5 类索引、父类索引、接口索引集合 this_class、super_class 、interfaces

其中类索引和父类索引都是指向常量池CONSTANT_Class_info常量的索引。接口索引集合由一个u2的接口计数器和多个指向CONSTANT_Class_info的索引组成。

2.6 字段表 field_info

字段包括类变量和实例变量,不包括方法内部生命的局部变量。结构如下:

类型名称数量描述
u2access_flags1访问标志
u2name_index1字段或方法名称在常量池的索引
u2descriptor_index1字段或方法描述符在常量池的索引
u2attributes_count1属性计数器
attributes_infoattributesattributes_count属性表

1. access_flags
字段的访问标识,具体如下:

标志名称标志值含义
ACC_PUBLIC0x0001字段是否为public
ACC_PRIVATE0x0002字段是否为private
ACC_PROTECTED0x0004字段是否为protected
ACC_STATIC0x0008字段是否为static
ACC_FINAL0x0010字段是否为final
ACC_VOLATILE0x0040字段是否为volatile
ACC_TRANSIENT0x0080字段是否为transient
ACC_SYNTHTIC0x1000字段是否由编译器自动产生
ACC_ENUM0x4000字段是否为enum

2. descriptor_index
字段和方法的描述符,主要用来表示字段的数据类型、方法的参数列表(数量、类型、顺序)、方法的返回值。根据描述符规则,基本数据类型(byte、short、int、long、float、double、char、boolean)以及无返回值的void都可以用一个大写字符来表示,引用数据类型用L家对象的全限定名来表示,数组类型,用[来表示,二维数组则是[[,详细如下:

标识符数据类型
Bbyte
Cchar
Ddouble
Ffloat
Iint
Jlong
Sshort
Zboolean
Vvoid
L对象类型,如Ljava/lang/object
[数组,如 int[],表示为[I

方法参数列表加返回值标志:

  • void add(int a,char[]) (I[C)V
  • String m1(String a) (Ljava/lang/String)Ljava/lang/String

字段集合中不会列出从父类或者接口中继承来的字段,但有可能列出原本Java代码里面不存在的字段,譬如在内部类中为了保持对外部类的访问,会自动添加指向外部类实例的字段。且字段是无法重载的。

3. attributes
比如final static int m = 10,10的值会存在属性表里。属性表详细介绍,后续讲解

2.7 方法表 method_info

方法表的数据结构与字段表相同,见上文中字段表的数据结构。

1. access_flags
方法的访问标志与字段的访问标识有些许区别,具体如下:

标志名称标志值含义
ACC_PUBLIC0x0001方法是否为public
ACC_PRIVATE0x0002方法是否为private
ACC_PROTECTED0x0004方法是否为protected
ACC_STATIC0x0008方法是否为static
ACC_FINAL0x0010方法是否为final
ACC_SYNCHRONIZED0x0020方法是否为synchronizedl
ACC_BRIDGE0x0040方法是否是有编译器产生的桥接方法
ACC_VARARGS0x0080方法是否接受不定参数
ACC_NATIVE0x0100方法是否为native
ACC_ABSTRACT0x0400方法是否为abstract
ACC_STRICTFP0x0800方法是否为strictfp
ACC_SYNTHTIC0x1000字段是否由编译器自动产生

如果子类没有重新父类方法,那个这个方法不会出现在子类的方法表中。但同样的,有可能会出现由编译器自动添加的方法,最典型的是类构造器“”和实例构造器“”

问:方法中的代码存到哪去了呢?存在方法表一个名为code的属性里面。

2.8属性表 attribute_info

属性表可以在字段表、方法表内部,也可以在最外层,与字段表平级。方法表与class文件中的其他数据结构不同,不再要求属性表有严格的顺序。属性表结构如下:

类型名称数量描述
u2attribute_name_index1属性名索引
u4attribute_length2属性长度
u1infoattribute_length属性内容

在jdk1.8中,jvm规范定义了23种属性,详情点击jvm官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值