1. Class 常量池(Class Constant Pool)
Class 常量池是 Class 文件中的资源仓库,用于存储 编译期生成的字面量(Literal)和符号引用(Symbolic References)。
它是 Java 字节码的一部分,用于支持类加载和运行时解析。
Class 常量池存储的内容
字面量(Literal)
基本类型常量(如
int
、float
)字符串常量(如
"Hello"
)
符号引用(Symbolic References)
类和接口的全限定名
字段名称和描述符
方法名称和描述符
接口方法的引用
Class 常量池的特点
存储在
.class
文件中,在编译时生成。是方法区的一部分,但不同 JDK 版本的存储方式不同。
在类加载时被 JVM 解析,用于支持方法解析、动态绑定等。
2. 运行时常量池(Runtime Constant Pool)
运行时常量池是 JVM 方法区的一部分,它在 类加载时 从 Class 常量池 解析并存储 实际的常量值和引用。
运行时常量池的作用
存储 Class 常量池解析后的内容
支持动态常量
支持
String.intern()
可在运行时动态修改
运行时常量池与 Class 常量池的关系
Class 常量池 → 运行时常量池
当类加载时,JVM 解析
.class
文件,将 Class 常量池的数据加载到运行时常量池中。
运行时常量池支持动态添加
运行时常量池不仅包含
.class
中的常量,还支持运行时动态添加,如String.intern()
。
3. 运行时常量池存储位置的变化
4. 如何查看 Class 常量池
可以使用 javap -v
命令反编译 .class
文件,查看 Class 常量池内容。
javap -v HelloWorld.class
示例输出:
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Class #16 // HelloWorld
#3 = Utf8 HelloWorld
#4 = Utf8 main
#5 = Utf8 ([Ljava/lang/String;)V
#6 = Class #17 // java/lang/Object
#7 = Utf8 java/lang/Object
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 SourceFile
#11 = Utf8 HelloWorld.java
#1 = Methodref #6.#15
:表示方法引用,指向Object.<init>()
。#3 = Utf8 HelloWorld
:存储类名HelloWorld
。#6 = Class #17
:表示java/lang/Object
的符号引用。
5. 运行时常量池的作用
(1) 字符串常量池
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
"hello"
被存储在 运行时常量池,s1
和s2
指向同一对象。
(2) String.intern()
String s1 = new String("hello").intern();
String s2 = "hello";
System.out.println(s1 == s2); // true
intern()
方法会检查运行时常量池,如果已存在相同的字符串,则返回池中的引用。
6. 总结
Class 常量池是 Class 文件的一部分,存储编译期的常量
运行时常量池加载 Class 常量池,并允许动态添加
JDK 1.7 及之后,运行时常量池被移至堆或 Metaspace,避免 PermGen OOM