运行时常量池是类加载时从class文件constant_pool映射而来、位于方法区(JDK7前)或元空间(JDK8+)的动态结构,负责存储并解析字面量(如字符串、static final基本类型)和符号引用(如类/方法名),在解析阶段将符号引用转为直接引用,并支持运行时扩展(如intern、动态类生成)。

运行时常量池(Runtime Constant Pool)是每个类或接口在加载到 JVM 后,从其 class 文件的 constant_pool 区域映射而来的内存区域,属于方法区(JDK 7 及以前)或元空间(JDK 8+)的一部分。它不是在编译期就“存放”字面量和符号引用的最终形态,而是在类加载的 解析阶段 和 初始化阶段 动态构建并填充的。
字面量(Literal)如何进入运行时常量池
字面量主要包括字符串字面量、final 基本类型常量、类/方法/字段的简单名称等。它们的处理方式不同:
-
字符串字面量:如
"hello",在类加载的准备阶段后、解析阶段中,JVM 遇到LDC指令时,会检查字符串常量池(String Table,独立于运行时常量池但与之联动)。若该字符串尚未存在,则在堆中创建对象,并将引用存入字符串常量池;同时,运行时常量池中对应项被解析为指向该字符串对象的引用(JDK 7+ 字符串常量池移到堆中,但运行时常量池仍保留对它的符号化记录)。 -
基本类型字面量(static final):如
public static final int MAX = 100;,在编译期就被内联进使用处(常量折叠),其值直接写入运行时常量池的CONSTANT_Integer_info等结构中,类加载时直接可用,无需运行时计算。
符号引用(Symbolic Reference)如何解析与存放
符号引用是 class 文件中对类、字段、方法的“逻辑描述”,比如 java/lang/Object.toString()Ljava/lang/String;。它们以 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info 等形式存在于 class 的常量池中,类加载时被原样复制进运行时常量池,但此时仍是未解析的符号。
- 在类加载的 解析阶段(Resolution),JVM 将这些符号引用转换为直接引用(Direct Reference):例如把类名解析为内存中的 Class 对象指针,把方法名+描述符解析为具体的方法入口地址(或虚方法表索引)。
- 解析是否立即执行取决于使用场景:有些符号引用(如静态字段、静态方法)在首次主动使用时才解析;而某些情况(如
invokedynamic)延迟到运行时由 Bootstrap Method 解析。 - 解析成功后,运行时常量池中对应槽位的内容会被更新为指向方法区中实际数据结构的指针(即直接引用),但原始符号信息在调试或反射中仍可追溯。
运行时常量池不是“静态容器”,而是动态解析上下文
它并非一次性填满后就不再变化。以下情况会导致其内容变化或扩展:
立即学习“Java免费学习笔记(深入)”;
- 通过
String.intern()显式将字符串加入字符串常量池,间接影响运行时常量池对字符串的引用关系。 - 动态生成类(如 ASM、CGLIB、Lambda 生成的内部类),其 class 字节码被 defineClass 加载后,对应的运行时常量池也会被构造出来。
- 反射调用(如
Class.forName、Method.invoke)可能触发额外的符号解析,甚至触发类的链接与初始化,从而影响相关常量池状态。
注意 JDK 版本差异
运行时常量池的物理归属随 JDK 演进而变化:
- JDK 6 及以前:方法区(永久代,PermGen),运行时常量池与类的其他元数据共存于 PermGen。
- JDK 7:运行时常量池移入堆(Heap),字符串常量池也移到堆中,两者更紧密协同。
- JDK 8+:方法区被元空间(Metaspace)取代,运行时常量池回归元空间管理(但字符串常量池仍在堆中),避免 PermGen OOM 问题。


















