JVM中的常量池简介

2023/06/12

1. 概述

当我们编译一个.java文件时,我们会得到一个带有.class扩展名的单独class文件。.class文件由几个部分组成,常量池就是其中之一。

在这个快速教程中,我们将探讨常量池的细节。此外,我们还将了解它支持哪些类型以及它如何格式化信息。

2. Java中的常量池

简单的说,常量池包含运行特定类的代码所需要的常量。基本上,它是一个类似于符号表的运行时数据结构。它是Java类文件中每个类或每个接口的运行时表示。

常量池的内容由编译器生成的符号引用组成,这些引用是从代码中引用的变量、方法、接口和类的名称。JVM使用它们将代码与它所依赖的其他类链接起来。

让我们使用一个简单的Java类来理解常量池的结构:

public class ConstantPool {

    public void sayHello() {
        System.out.println("Hello World");
    }
}

要查看常量池的内容,我们需要先编译文件,然后运行以下命令:

javap -v name.class

这将生成:

   #1 = Methodref          #6.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #17            // Hello World
   #4 = Methodref          #18.#19        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #20            // cn/tuyucheng/taketoday/jvm/ConstantPool
   #6 = Class              #21            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               sayHello
  #12 = Utf8               SourceFile
  #13 = Utf8               ConstantPool.java
  #14 = NameAndType        #7:#8          // "<init>":()V
  #15 = Class              #22            // java/lang/System
  #16 = NameAndType        #23:#24        // out:Ljava/io/PrintStream;
  #17 = Utf8               Hello World
  #18 = Class              #25            // java/io/PrintStream
  #19 = NameAndType        #26:#27        // println:(Ljava/lang/String;)V
  #20 = Utf8               cn/tuyucheng/taketoday/jvm/ConstantPool
  #21 = Utf8               java/lang/Object
  #22 = Utf8               java/lang/System
  #23 = Utf8               out
  #24 = Utf8               Ljava/io/PrintStream;
  #25 = Utf8               java/io/PrintStream
  #26 = Utf8               println
  #27 = Utf8               (Ljava/lang/String;)V

#n表示对常量池的引用。#17是对“Hello World”字符串的符号引用,#18是System.out,#19是println。同样,#8突出显示方法的返回类型为void,#20是完全限定的类名。

需要注意的是,常量池表是从索引1开始的,索引值0被认为是无效索引

2.1 类型

常量池支持多种类型:

  • Integer、Float:32位常量
  • Double、Long:64位常量
  • String:一个16位字符串常量,指向池中包含实际字节的另一个条目
  • Class:包含完全限定的类名
  • Utf8:字节流
  • NameAndType:一对以冒号分隔的值,第一个条目表示名称,第二个条目表示类型
  • Fieldref、Methodref、InterfaceMethodref:一对以点分隔的值,第一个值指向Class条目,而第二个值指向NameAndType条目

boolean、short和byte等其他类型呢?这些类型在池中表示为Integer常量。

2.2 格式

表中的每个条目都遵循通用格式:

cp_info {
    u1 tag;
    u1 info[];
}

起始1字节标记表示常量的类型。一旦JVM获取并拦截了标记,它就知道标记后面是什么。通常,标记后跟2个或多个字节来携带有关该常量的信息。

让我们看一下一些类型和它们的标签索引:

  • Utf8:1
  • Integer:3
  • Float:4
  • Long:5
  • Double:6
  • Class引用:7
  • String引用:8

任何类或接口的常量池只有在JVM加载完成后才会创建

3. 总结

在这篇快速文章中,我们了解了JVM中的常量池。我们已经看到它包含用于定位实际对象的符号引用。此外,我们还了解池如何格式化有关常量及其类型的信息。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章