1. 简介
当我们编译一个 *.java
文件时,会生成一个扩展名为 *.class
的类文件。这个 .class
文件由多个部分组成,其中一部分就是常量池(Constant Pool)。
在本篇文章中,我们将深入探讨 JVM 常量池的相关细节,包括它支持的数据类型以及信息的存储格式。
2. Java 中的常量池
简单来说,常量池是用于保存当前类运行所需的各种常量的数据结构。你可以把它理解为一种运行时使用的符号表(Symbol Table),它是每个类或接口在 .class
文件中的运行时表示形式。
常量池的内容是由编译器生成的符号引用构成的,这些引用包括变量名、方法名、接口名和类名等。JVM 在运行时会使用这些引用去链接当前类与其他依赖类之间的关系。
我们通过一个简单的 Java 类来查看常量池的结构:
public class ConstantPool {
public void sayHello() {
System.out.println("Hello World");
}
}
要查看该类的常量池内容,我们需要先进行编译,然后执行如下命令:
javap -v ConstantPool.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 // com/baeldung/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 com/baeldung/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 位字符串常量,指向池中另一个 UTF8 字符串条目 |
Class |
完整类名 |
Utf8 |
字节流,表示字符串 |
NameAndType |
冒号分隔的一对值,分别表示名称和描述符 |
Fieldref , Methodref , InterfaceMethodref |
点号分隔的一对值,分别指向 Class 和 NameAndType 条目 |
❓那像 boolean
、short
、byte
这些基本类型呢?
✅ 这些类型在常量池中统一用 Integer
表示。
2.2. 数据格式
常量池中每个条目的结构都遵循以下通用格式:
cp_info {
u1 tag;
u1 info[];
}
其中:
tag
是一个字节,用来标识该条目是什么类型的常量;info[]
是后续的实际数据。
举几个常见类型的 tag 值:
类型 | Tag 值 |
---|---|
Utf8 |
1 |
Integer |
3 |
Float |
4 |
Long |
5 |
Double |
6 |
Class 引用 |
7 |
String 引用 |
8 |
✅ 只有当 JVM 完成类加载之后,才会创建对应的常量池。
3. 总结
在这篇文章中,我们了解了 JVM 中的常量池机制。它本质上是一个运行时符号表,保存了类运行所需的各类符号引用。我们也看到了常量池如何组织不同类型的信息,并通过 tag 标识不同的条目类型。
如果你想进一步研究,可以访问 GitHub 获取示例代码: 🔗 https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-jvm-2