升职高薪之路——JVM常量池超详细总结

318 阅读9分钟

这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战

🔸一、常量池总数

  • 常量池大小(cp_info_count):常量池是class文件中第一个出现的变长结构。既然是池,就有大小,常量池大小由两个字节表示(u2)。

  • 假设常量池大小为n,常量池真正有效的索引是1n-1。如果constant pool_count等于10,constant_pool数组的有效索引值是19。0属于保留索引,可供特殊情况使用。

 常量池总数数据结构

image.png

🔺二、常量池【静态常量池(很多时候我们说的静态常量池指的就是class文件中的常量池)】

  • Java虚拟机指令不依赖于类、接口、类实例或数组的运行时布局,而是依赖常量池表中的符号信息
  • 如创建对象的new指令,需要指定对象的类型描述符,new指令的操作数的值为该类型描述符对应的常量在常量池中的索引,虚拟机将根据常量信息到方法区寻找对应的class元数据。

常量池中主要存放两大类常量:

  • 字面量(Literal) 字符串(UTF-8)、被声明为final的常量值(整数值、浮点)等

  • 符号引用(Symbolic References)

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

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

方法的名称和描述符

方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)

动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)

1. 常量池数据结构

  • 常量池是一个数组。里面每一个项为一个具体的常量

image.png

  • 常量池中的每一项为一个具体的常量,其数据结构如下 image.png

  • 根据《Java虚拟机规范》规定,常量池表中所有常量项的结构都包含一个tag项,tag值用于标志一个常量是哪种常量结构。只有根据tag确定常量的结构,才能根据常量结构计算常量所占用的字节数。

2. 根据tag的不同 ,JDK8中常量具体分为14种

常量类型tag描述
CONSTANT_Class_info7类或接口的符号引用
CONSTANT_Fieldref_info9字段的符号引用
CONSTANT_Methodref_info10类中方法的符号引用
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用
CONSTANT_String_info8字符串类型字面量
CONSTANT_Integer_info3整型字面量
CONSTANT_Float_info4浮点型字面量
CONSTANT_Long_info5长整形字面量
CONSTANT_Double_info6双精度浮点型字面量
CONSTANT_NameAndType_info12字段与字段类型或方法与方法类型的符号引用
CONSTANT_Utf8_info1UTF-8编码字符串
CONSTANT_MethodHandle_info15表示方法句柄
CONSTANT_MethodType_info16表示方法类型
CONSTANT_InvokeDynamic_info18表示一个动态方法调用点

3. 字段描述符

  • 字段描述符(field descriptor),用来表示类、实例或局部变量

  • 符号列表为:

字符类型含义
Bbyte有符号的字节型数
---------------------------------------------------------------
Cchar基本多文种平面中的Unicode字符码点,UTF-16编码双精度浮点数单精度浮点数
Ddouble双精度浮点数
Ffloat单精度浮点数
Iint整型数
Jlong长整数
L ClassName ;referenceClassName 类的实例
Sshort有符号短整数
Zboolean布尔值true/false
[reference一个一维数组
  • 例如:

  • 描述int实例变量的描述符是I

  • object类型的实例,其描述符是Ljava/lang/object;

  • 三维数组double d[][][]类型的实例变量,其描述符为[[[D

4. 方法描述符

  • 方法描述符(method descriptor)包含0个或多个参数描述符(parameter descriptor)以及个返回值描述符(return descriptor)参数描述符表示该方法所接受的参数类型,如果该方法有返回值的话,那么返回值描述符则表示该方法的返回值类型
  • 实例方法除了传递自身定义的参数,还需要额外传递参数this ,但是这一点不是由方法描述符来表达,参数this的传递由Java虚拟机中调用实例方法所使用的指令来实现。
  • 类方法和实例方法描述符都是一样

例如:

方法为:Object m(int i, double d, Thread t) { .. }

描述符为:(IDLjava/lang/Thread;)Ljava/lang/Object

实例方法除了递自身定义的参数,还需要额外传递参数this ,但是这一点不是由方法描述符来表达,参数this的传递由Java虚拟机中调用实例方法所使用的指令来实现

5. CONSTANT_Class_info

CONSTANT_Class_info {

    u1 tag;

    u2 name_index;

}

解释

l 类、接口、数组、枚举、注解的符号引用

l 一个有效的数组类型描述符中描述的数组,其维度必须小于等于255

l 类中或接口中使用到了其他的类,只有在类中实际使用到了该类时,该类的信息才会在常量池中有对应的CONSTANT_Class_info常量池项;

l 项约束

l u2 name_index:必须是一个CONSTANT_Utf8_info常量,表示类(或者接口)的全限定名

6. CONSTANT_Fieldref_info

CONSTANT_Fieldref_info {

    u1 tag;

    u2 class_index;

    u2 name_and_type_index;

}

解释

  • 字段的符号引用
  • static final修饰的字面量(即static final修饰的数据类型是String、基本数据类型【不含基本数据类型的包装类】)不会出现Fieldref_info
  • 所有使用的字段(含赋值的字段、再其它地方使用的字段、使用了其它类的字段)

项约束

  • u2 class_index:指向的常量必须是一个CONSTANT_Class_Info常量,表示当前字段所在的类的类名。
  • u2 name_and_type_index:指向的常量必须是一个CONSTANT_NameAndType_info常量,表示当前字段的名字和类型描述符。

7. CONSTANT_Methodref_info

CONSTANT_Methodref_info {

    u1 tag;

    u2 class_index;

    u2 name_and_type_index;

}

解释

  • 类中方法的符号引用
  • 所有使用的方法(也就是必须调用)
  • 默认调用父类无参构造方法<init>:()V

项约束

  • u2 class_index:指向的常量必须是一个CONSTANT_Class_Info常量,且必须是类(而不能是接口),表示当前方法所在的类的类名。
  • u2 name_and_type_index:指向的常量必须是一个CONSTANT_NameAndType_info常量,表示当前方法的名字和类型描述符。

8. CONSTANT_InterfaceMethodref_info

CONSTANT_InterfaceMethodref_info {

    u1 tag;

    u2 class_index;

    u2 name_and_type_index;

}

解释

  • 接口中方法的符号引用

项约束

  • u2 class_index:指向的常量必须是一个CONSTANT_Class_Info常量,且必须是接口类型,表示当前接口方法所属的接口的类名。
  • u2 name_and_type_index:指向的常量必须是一个CONSTANT_NameAndType_info常量,表示当前接口方法的名字和。  

9. CONSTANT_String_info

CONSTANT_String_info {

    u1 tag;

    u2 string_index;

}

解释

  • 字符串类型字面量
  • static final 修饰String数据类型
  • 其它的出现的string字面量

项约束

  • u2 string_index:值为常量池中某个常量的索引,该索引指向的常量必须是一个CONSTANT_Utf8_info常量。  

10. CONSTANT_Integer_info

CONSTANT_Integer_info {

    u1 tag;

    u4 bytes;

}

解释

整型字面量

  • static final 修饰byte、char、short、boolean、int数据类型
  • 可用于描述boolean(1,0)、byte(-2727-1【-128127】)、int、short(-215215-1【-3276832767】)、char(0~216-1【0-65535】)
  • 非static final修饰,在-3276832767(即-215215-1,short的范围内)范围,则直接进入字节码指令内部,超出此范围进入直接进CONSTANT_Integer_info常量池

项约束

  • u4 bytes:bytes转为10进制数就是这个常量所表示的整型值。

11. CONSTANT_Float_info

CONSTANT_Float_info {

    u1 tag;

    u4 bytes;
    }

解释

  • static final 修饰 float

  • 其它的出现的字面量

约束

  • u4 bytes:bytes转为10进制数就是这个常量所表示的浮点值。

12. CONSTANT_Long_info

CONSTANT_Long_info {

CONSTANT_Long_info {

    u1 tag;

    u4 high_bytes;

    u4 low_bytes;

}

解释

  • 长整形字面量
  • static final 修饰 Long
  • 其它的出现的字面量

约束

  • u4 high_bytes:高32位区
  • u4 low_bytes:低32位区

13. CONSTANT_Double_info

CONSTANT_Double_info {

    u1 tag;

    u4 high_bytes;

    u4 low_bytes;

}

解释

  • 双精度浮点型字面量

  • static final 修饰 double

  • 其它的出现的字面量

约束

  • u4 high_bytes:高32位区
  • u4 low_bytes:低32位区

14. 字面量和 Fieldref_info的出现的规则

  • 字面量:数据类型是String以及基本数据类型(不包含基本数据类型包装类)\

引用类型(非字面量,含基本数据类型的包装类型):\

1——static final修饰的字面量在本类和其它类里面都会以字面量类型的常量出现
2——非static final修饰的字面量在本类以字面量类型的常量出现和Fieldref_info类型的常量出现
3——非static final修饰的字面量在其它类以Fieldref_info类型的常量出现
4——引用类型在本类和其它类里面都会Fieldref_info
5——static final修饰的字面量不会出现Fieldref_info
6——static final修饰的基本数据类型包装类会同时出现Fieldref_info和包装类的字面量常量类型

15. CONSTANT_NameAndType_info

CONSTANT_NameAndType_info {

    u1 tag;

    u2 name_index;

    u2 descriptor_index;

}

解释

  • 字段与字段类型或方法与方法类型的符号引用

约束项

  • u2 name_index:必须是CONSTANT_Utf8_info结构的常量,表示字段与字段类型或方法与方法类型的的名称。
  • 构造方法名称统一使用
  • u2 descriptor_index:必须是CONSTANT_Utf8_info结构的常量,表示一个有效的字段描述符或方法描述符。

16. CONSTANT_Utf8_info

CONSTANT_Utf8_info {

    u1 tag;

    u2 length;

    u1 bytes[length];

}

解释

  • UTF-8编码字符串

约束

  • u2 length:该值指明了bytes[]数组的长度
  • u1 bytes[length]:是表示字符串值的byte数组

17. CONSTANT_MethodHandle_info【动态类型语言支持】【认知】

CONSTANT_MethodHandle_info {

    u1 tag;

    u1 reference_kind;

    u2 reference_index;

}

表示方法句柄,这是虚拟机为实现动态调用invokedynamic指令所增加的常量结构。

  • u1 reference_kind:取值范围为1~9,包括1和9,表示方法句柄的类型

image.png

  • u2 reference_index:其值为指向常量池中某个常量的索引

18. CONSTANT_MethodType_info【动态类型语言支持】【认知】

CONSTANT_MethodType_info {

    u1 tag;

    u2 descriptor_index;

}

  • 表示方法类型,与CONSTANT_MethodHandle_info结构一样,也是虚拟机为实现动态调用invokedynamic指令所增加的常量结构。

  • u2 descriptor_index:必须是CONSTANT_Utf8_info结构的常量,指向常量池中的某一CONSTANT_Utf8_info结构的常量。

19. CONSTANT_InvokeDynamic_info【动态类型语言支持】【认知】

CONSTANT_InvokeDynamic_info {

    u1 tag;

    u2 bootstrap_method_attr_index;

    u2 name_and_type_index;

}

表示一个动态方法调用点,即invokedynamic指令用到的引导方法bootstrap method以及引导方法所用到的动态调用名称、参数、返回类型。

  • u2 bootstrap_method_attr_index:指向class文件结构属性表中引导方法表的某个引导方法
  • u2 name_and_type_index:指向的常量必须是一个CONSTANT_NameAndType_info常量