jvm类文件的结构2-常量池与类访问的标志

常量池

紧接着版本号的是常量池
常量池有以下几点特征:

  1. 常量池是Class文件结构中与其他项目关联最多的数据类型

  2. 常量池是占用Class文件空间最大的数据项目之一

  3. 常量池是Class文件中第一个出现的表类型数据项目

计数值

由于常量池中常量的数量是不固定的, 所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)。与Java语言习惯不一样的是,这个容量计数是从1而不是0开始的。
制定Class文件格式规范时,将第0项常量空出来是有特殊考虑的:
某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”,就可以把索引值置为0来表示。
Class文件结构中只有常量池的容量计数是从1开始的,对于其他集合类型,包括接口索引集合、字段表集合、方法表集合等的容量计数都与一般习惯相同,是从0开始的。

字面量和符号引用

常量池之中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。

字面量

字面量比较接近于Java语言层面的常量概念,如文本字符串、被声明为final的常量值等。

符号引用

符号引用主要属于编译原理方面的概念, 包括了下面三类常量:

  • 类和接口的全限定名(Fully Qualified Name)

  • 字段的名称和描述符(Descriptor)

  • 方法的名称和描述符

Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。
也就是说,在Class文件中不会保存各个方法和字段的最终内存布局信息,因此这些字段和方法的符号引用不经过转换的话是无法直接被虚拟机使用的。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址之中。

常量表

常量池中的每一项常量都是一个表,共有11种结构各不相同的表结构数据,这11种表都有一个共同的特点,就是表开始的第一位是一个u1类型的标志位(tag,取值为1至12,缺少标志为2的数据类型),代表当前这个常量属于哪种常量类型

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用

常量表的具体内容

CONSTANT_Utf8_info

|项目|类型|描述|
|tag|u1|值为1|
|length|u2|UTF-8编码的字符串占用的字节数|
|bytes|u1|长度为length的UTF-8编码的字符串|

CONSTANT_Integer_info

|项目|类型|描述|
|tag|u1|值为3|
|bytes|u4|按照高位在前存储的int值|

CONSTANT_Float_info

|项目|类型|描述|
|tag|u1|值为4|
|bytes|u4|按照高位在前存储的float值|

CONSTANT_Long_info

|项目|类型|描述|
|tag|u1|值为5|
|bytes|u8|按照高位在前存储的long值|

CONSTANT_Double_info

|项目|类型|描述|
|tag|u1|值为6|
|bytes|u8|按照高位在前存储的double值|

CONSTANT_Class_info

|项目|类型|描述|
|tag|u1|值为7|
|index|u2|指向全限定名常量项的索引|

CONSTANT_String_info

|项目|类型|描述|
|tag|u1|值为8|
|index|u2|指向字符串字面量的索引|

CONSTANT_Fieldref_info

|项目|类型|描述|
|tag|u1|值为9|
|index|u2|指向声明字段的类或接口描述符CONSTANT_Class_info的索引项|
|index|u2|指向字段描述符CONSTANT_NameAndType的索引项|

CONSTANT_Methodref_info

|项目|类型|描述|
|tag|u1|值为10|
|index|u2|指向声明字段的类或接口描述符CONSTANT_Class_info的索引项|
|index|u2|指向字段描述符CONSTANT_NameAndType的索引项|

CONSTANT_InterfaceMethodref_info

|项目|类型|描述|
|tag|u1|值为11|
|index|u2|指向声明字段的类或接口描述符CONSTANT_Class_info的索引项|
|index|u2|指向字段描述符CONSTANT_NameAndType的索引项|

CONSTANT_NameAndType_info

|项目|类型|描述|
|tag|u1|值为12|
|index|u2|指向该字段或方法名称常量项的索引|
|index|u2|指向该字段或方法描述符常量项的索引|

javap -verbose

我们可以使用javap -verbose来查看一个Class文件的字节码信息。

访问标志

常量池之后的2个字节代表访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息,包括:

  • 这个Class是类还是接口

  • 是否定义为public类型

….

以下是Java中的访问标志:

标志名称 标志值 含义
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否被声明为final,只有类可设置
ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令,JDK1.2之后编译出来的类这个标志为真
ACC_INTERFACE 0x0200 标识这是一个接口
ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口或抽象类来说,这个值为真,其他类值为假
ACC_SYNTHETIC 0x1000 标识这个类并非由用户代码产生的
ACC_ANNOTATION 0x2000 标识这是一个注解
ACC_ENUM 0x4000 标识这是一个枚举

access_flags一共有32个标志位可以使用,当前只定义了其中的8个。
没有使用到的标志位要求一律为0。
这些标志位以或的方式集成到一起。