高亮的杂货铺

JVM从0到1-class文件常量池

2020-03-14 · 3 min read
JVM Java

常量池可以比喻为class文件里的资源仓库,他是class文件结构中与其他项目关联最多的数据,通常也是占据class文件空间最大的项目。

由于常量池中的常量的数量是不固定的,所以在入口处需要防止一个u2类型的数据,代表常量池容量计数器。需要注意的是,这个计数器从1开始,而不是0,所以常量池计数器的值,实际表示的常量池中的常量数量+1。

常量池中主要存放两种常量,字面量(Literal)和符号引用(Symbolic References)。字面量比较接近Java语言层面常量的概念,而符号引用则属于编译原理方面的概念,主要包括一下几种

  • 被模块导出或者开放的包(Package)
  • 类和接口的全限定名(Full Qualified Name)
  • 字段的名称和描述(Descriptor)
  • 方法的名称和描述
  • 方法句柄和方法类型(Method Handle,Method Type, Invoke Dynamic)
  • 动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-computed Constant)
    Javac编译器没有连接这一个步骤,而是在虚拟机加载class文件时及逆行动态连接的,所以class文件中没有保存各个方法、字段在内存中的布局信息,这些字段、方法的引用是在虚拟机运行期间进行转换的。

常量池中的每一个项目都是一个表,最初设计中只有11种,后续不断扩展,截至到JDK13,常量表中共有17中不同类型的常量。

这其中的每一种类型,都具有独立的结构,因此常量池的结构十分复杂。

CONSTANT_Utf8_info


legth说明这这个UTF-8编码的字符串长度是多少字节,他后面紧跟着的是长度为length个字节的UTF-8缩略编码的连续数据。 UTF-8缩略编码与普通UTF-8编码的区别是:从'\u0001'到'\u007f'之间的字符(相当于1~127的ASCII码)的缩略编码使用一个字节表示,从'\u0080'到'\u07ff'之间的所有字符的缩略编码用两个字节表示,从'\u0800'开始到'\uffff'之间的所有字符的缩略编码就按照普通UTF-8编码规则使用三个字节表示。

由于class文件中方法和字段等都需要引用这个类型的常量来描述名称,所以这个常量的最大长度也就是Java中方法和字段的最大长度,u2,这也就意味着最大值为65535。所以这也意味着Java程序中变量和方法名的大小无法超过64KB。

CONSTANT_Class_info


这种常量表示一个类或者接口的符号引用,其中,name_index是常量池的索引,指向一个常量池中的CONSTANT_Utf8_info类型常量,代表了这个类的全限定名。在字段表、方法表、和属性表中,会引用常量表中的符号引用来进行表达。

其他字段结构