写点什么

JVM 进阶 (十九)——Class 文件常量池

  • 2022 年 5 月 26 日
  • 本文字数:1241 字

    阅读完需:约 4 分钟

JVM进阶(十九)——Class文件常量池

在上一博文《JVM进阶(十八)——初识Class文件》中,我们了解了 Class 文件的一些基础知识。他的整个内部结构就是一张很大的表,我们就是从这张表入手,一一分析每个部分的结构。继续看这张表:



接着上一博文所说,魔数后面分别是次版本号和主版本号。由上图可知其分别占用两个字节。



被蓝色框框住的就是次版本号,划红线的就是主版本号。再次说明,Class 文件内部的数据是按照规则紧凑排列的,中间不会有空隙。


接下来就是说明常量的个数了。代表着常量池中有多少个常量,由于常量池中的常量数量不确定,所以才会有这个数据项。依然看上图可知该数据项是占用 2 个字节,因此顺着主版本号往后面数两个字节得到:0x002E(16 进制),即十进制的 51,也就是说常量池中有 50 项常量,索引从 1 到 50。


这里所指的常量与JAVA代码中所说的常量有所不同,这里的常量主要包括字面量和符号引用,这两个概念很好理解。


字面量跟 JAVA 代码中的常量概念类似,如字符串、常量的值等等。


符号引用指的是类与接口的全限定名、字段、方法的名词和描述符。可以暂时理解为类、接口、字段、方法的名字。这里我们来回忆一下类加载机制中的解析阶段:他是将符号引用转化为直接引用。直接引用指的就是可以直接指向目标的指针。可以粗略的理解为:符号引用只是用一些符号来描述他要引用的目标,而直接引用才是真正的指向了他要引用的目标。


在常量池中的每个数据项都是以表的形式存在的,这里每个表都会有一个标志位 tag,来说明自己的是哪一类型的数据。如图:



我们来看下面的代码:


public class Main {  private String name;  private int age;  public void setName(String nage) {    this.name = name;  }  public void getAge() {    return age;  }  public static void main (String[] args) {    Main m = new Main();    m.setName('No Silver Bullet');    System.out.println(m.getAge());  }}
复制代码


根据以上知识和代码,我们继续来看看Class文件接下来的数据。紧接着常量池数量之后的便是常量表了。刚刚也说了,每个表都会有一个一个字节的标志位,那么常量池数量0x002E之后一个字节便是0x0A,这个就是标志位,十进制是 10,查表可知是个方法的符号引用。他的表结构如下:



因此后面还有 4 个字节是属于该表的,我们接着看是0x000B0x001C,也就是说他的CONSTANT_Class_info索引项是 11;CONSTANT_NameAndType的索引项是 28,也就是常量池中第 11 项常量和 28 项常量,我们这里就通过工具来看了。找到第 11 项常量,查看 11 项常量的表结构,继续使用刚刚那样的寻找方法,一直找到标志位为 1 的常量项,也就是CONSTANT_Utf8_info的表结构,这样就可以得出我们最开始查看的那个表结构的一些具体信息了。


如果觉得查看过程繁琐,可以采用javap -verbose Main来查看:



如上图:第 1 项有指向第 11 和 28 项的索引,他们的值分别是后面的字符串,代表的是一个默认的空的构造函数。


查看跟踪的过程比较枯燥无味,但这也是我们深入了解虚拟机的一个非常重要的基础,大家可查阅更多的相关资料进行学习,有困难,迎难而上才会成长!



发布于: 刚刚阅读数: 3
用户头像

No Silver Bullet 2021.07.09 加入

岂曰无衣 与子同袍

评论

发布
暂无评论
JVM进阶(十九)——Class文件常量池_JVM_No Silver Bullet_InfoQ写作社区