JDK8以后,运行时常量池逻辑上属于方法区;但:
- 其中的字符串常量池实际位置移至到了java堆;
- 其中一些符号引用也存储到了元空间;
- 字符串常量池,元空间,运行时常量区的具体关系请看这篇博客:
JDK8+后,运行时常量池、字符串常量池和元空间的关-CSDN博客
JVM 方法区(Method Area) 中的 运行时常量池(Runtime Constant Pool) 存储的数据来源于 类文件常量池(Class File Constant Pool),并在运行时进行动态解析和扩展。具体存储的内容可以分为以下几类:
1. 类文件常量池的原始数据(编译期生成)
在 .class
文件中,常量池(Constant Pool)存储了各种符号信息,JVM 在加载类时会将其解析到运行时常量池。主要包括:
(1) 字面量(Literals)
-
字符串常量(
String
):如"Hello"
(最终可能被放入字符串池String Table
)。 -
数值常量:
-
整型(
int
,long
,short
,byte
,char
):如123
,0x1F
。 -
浮点型(
float
,double
):如3.14
,2.71828
。
-
-
final
常量(无论是否static
):-
static final int MAX = 100;
(静态常量) -
final String NAME = "Java";
(实例常量)
-
(2) 符号引用(Symbolic References)
-
类和接口的全限定名(Fully Qualified Name):如
java/lang/String
。 -
字段的名称和描述符(Field Name & Descriptor):
-
如
Ljava/lang/String;
(字段类型描述符)。
-
-
方法的名称和描述符(Method Name & Descriptor):
-
如
main([Ljava/lang/String;)V
(main
方法的描述符)。
-
-
方法句柄(MethodHandle)和动态调用点(InvokeDynamic)信息(Java 7+)。
2. 运行时动态解析的数据
在类加载、链接(验证、准备、解析)阶段,JVM 会将符号引用转换为直接引用:
-
类/接口的解析:将
java/lang/Object
转换为实际类对象的引用。 -
字段解析:将字段符号引用转换为内存偏移量或
Field
对象。 -
方法解析:将方法符号引用转换为方法入口地址(
Method
对象或 JNI 指针)。 -
动态计算的常量(Java 8+):
-
如
CONSTANT_Dynamic
(invokedynamic
指令使用的动态常量)。
-
3. 动态生成的常量
-
String.intern()
的字符串:-
调用
"abc".intern()
后,字符串会被放入字符串池(String Table),逻辑上属于运行时常量池的一部分(但 HotSpot 的字符串池在堆中)。
-
-
动态代理类、Lambda 表达式生成的类信息(可能涉及新的常量池条目)。
-
Class
对象引用:如String.class
的运行时表示。
4. 其他 JVM 内部使用的数据
-
基本类型的包装类缓存:
如Integer.valueOf(1)
会缓存-128~127
,这些值可能被复用。 -
特殊常量:
null
的表示,以及某些 JVM 内部使用的标记常量(如异常类名)。
总结:运行时常量池存储哪些数据?
数据类型 | 示例 | 是否动态解析 |
---|---|---|
字符串常量 | "Hello" | 可能 intern() |
数值常量 | 123 , 3.14 | 否 |
final 常量 | static final int X=1 | 可能内联优化 |
类和接口全限定名 | java/lang/String | 解析为 Class |
字段/方法符号引用 | Ljava/lang/String; | 解析为直接引用 |
动态计算的常量 | CONSTANT_Dynamic (Java 8+) | 运行时计算 |
String.intern() | "abc".intern() | 动态添加 |
关键点
-
运行时常量池不仅存储静态常量,还包括所有类文件的常量池信息 + 运行时解析的数据。
-
符号引用会在类加载时解析,变成直接引用(如内存地址、方法指针等)。
-
字符串常量可能被
intern()
并放入字符串池(HotSpot 中字符串池在堆,但逻辑属于运行时常量池)。 -
动态语言支持(如
invokedynamic
) 会新增动态计算的常量。
如果你对某部分内容(如字符串池、符号引用解析)想深入了解,可以进一步讨论!
常见误区澄清
-
误区:“运行时常量池只存静态常量”。 事实:它存储所有类文件常量池的内容,包括动态解析的引用和字面量。
-
字符串池的特殊性: 在HotSpot JVM中,字符串池(String Table)实际位于堆内存,而非方法区,但逻辑上属于运行时常量池的一部分。
静态 vs. 非静态常量
-
静态常量(
static final
): 例如public static final int MAX = 100;
,这类常量会被存入运行时常量池,但它们的值可能进一步被内联到使用它们的代码中(编译期优化)。 -
非静态常量(实例级
final
常量): 例如private final String name = "Hello";
,虽然字面量"Hello"
会存储在运行时常量池中,但该常量值是与对象实例绑定的,需要通过实例访问。