JVM方法区的运行时常量区到底存储哪些数据?
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"会存储在运行时常量池中,但该常量值是与对象实例绑定的,需要通过实例访问。
