运行时常量池在类加载的解析阶段完成后、初始化开始前被创建并存放于方法区(jdk 8+为元空间)。它源自class文件常量池,经准备和解析阶段将符号引用转为直接引用,支持动态扩展,且与堆中的字符串常量池分离管理。

运行时常量池在类加载的“解析阶段”之后、初始化之前被分配并存放,确切地说,是在类加载过程的 连接阶段中的“准备”与“解析”完成后,由 JVM 为该类在方法区(JDK 8+ 为元空间)中创建对应的运行时常量池结构。
运行时常量池的分配时机
它不是在编译期就分配内存,也不是在 new 对象时才创建,而是严格绑定类的加载生命周期:
- 类首次被主动引用时触发加载:例如首次调用静态方法、访问静态字段(非编译期常量)、反射获取 Class 对象等,会启动类加载流程;
- 加载 → 验证 → 准备 → 解析 → 初始化:运行时常量池在“准备”阶段为符号引用预留空间,在“解析”阶段将 class 文件常量池中的符号引用(如类名、字段名、方法名)逐步解析为直接引用,并填充到运行时常量池中;
- 分配发生在解析完成之后、初始化开始之前:此时运行时常量池已具备完整结构,可被后续字节码指令(如 ldc、ldc_w)访问。
运行时常量池的存放位置(按 JDK 版本)
它的物理位置取决于方法区的具体实现,且与字符串常量池分离管理:
- JDK 6 及以前:位于永久代(PermGen),即方法区;
- JDK 7:运行时常量池仍在永久代,但字符串常量池被移至堆中;
- JDK 8 及以后:运行时常量池存放在 元空间(Metaspace),即本地内存中的方法区;字符串常量池仍保留在堆中。
关键细节说明
运行时常量池不是一次性全量加载,而是按需解析和动态扩充:
- class 文件常量池中未被使用的符号引用,可能始终不被解析,也就不占用运行时常量池实际内存;
- intern() 调用会向字符串常量池(堆中)添加字符串实例,但不影响运行时常量池本身的内容;
- 运行时常量池支持运行期动态加入常量(如通过 MethodHandle 或 LambdaMetafactory 生成的引导方法信息),体现其“动态性”;
- 若元空间不足(JDK 8+)或永久代耗尽(JDK 7-),JVM 抛出 OutOfMemoryError: Metaspace 或 OutOfMemoryError: PermGen space,而非堆溢出。











