运行时常量池是类加载时从class文件常量池映射到方法区(JDK 8+为元空间)的动态内存区域,存储字面量(如字符串引用、基本类型值)和符号引用(类/字段/方法名等),支持解析为直接引用、字符串驻留及运行时动态扩展。

运行时常量池(Runtime Constant Pool)是每个类或接口在加载到 JVM 时,从其 class 文件的 constant_pool 区域映射而来的内存区域,属于方法区(JDK 7 及以前在永久代,JDK 8+ 在元空间,但逻辑上仍属方法区)。它不仅存放编译期生成的字面量(如字符串字面量、数字常量),也存放符号引用(如类名、字段名、方法名及描述符)。但要注意:**字面量与符号引用的存放方式和时机并不完全相同,且部分字面量(尤其是字符串)会经历“从常量池到堆”的迁移过程。**
字面量的存放:静态结构 + 动态入池
class 文件中已存在的字面量(如 "hello"、123、true)在类加载的**解析阶段**会被直接写入运行时常量池——这是初始的、只读的结构化存储。
- 基本类型字面量(整数、浮点、字符、布尔)和 null 直接以对应值形式存入常量池项(CONSTANT_Integer_info 等)
- 字符串字面量在 class 文件中以 CONSTANT_String_info 指向一个 CONSTANT_Utf8_info;加载时,JVM 会用该 UTF-8 字节序列构造一个 String 实例,并尝试将其放入字符串常量池(String Table)
- 注意:字符串常量池(String Table)是运行时常量池的**逻辑子集**,但从 JDK 7 开始,它被移到了 Java 堆中(不再是方法区的一部分),所以严格来说:字符串字面量的引用存于运行时常量池,而字符串对象本身存于堆
符号引用的存放:原样保留,延迟解析
符号引用(Symbolic References)包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符等。它们在类加载的**准备阶段之后、解析阶段之前**,以原始二进制形式(如 UTF-8 字节数组)直接存入运行时常量池,不做任何解析或链接。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 例如:对 java/lang/Object.toString() 的调用,在 class 文件中记为 CONSTANT_Methodref_info,指向类名和方法名/描述符的 CONSTANT_NameAndType_info 和 CONSTANT_Class_info
- 这些符号引用保持“未绑定”状态,直到首次主动使用(如第一次执行 invokevirtual 指令)时才触发解析(Resolution),将符号引用转换为直接引用(如内存地址、偏移量)
- 解析结果通常不写回运行时常量池(某些 JVM 实现可能缓存,但规范不强制),而是存于方法区其他结构(如类的运行时数据结构)中
运行时常量池不是“只读池”,支持动态扩展
与 class 文件中的常量池不同,运行时常量池是可动态修改的。最典型的例子就是 String.intern():
立即学习“Java免费学习笔记(深入)”;
- 若字符串对象内容已在字符串常量池中存在,则返回池中引用
- 否则,将该字符串对象引用加入字符串常量池(JDK 7+ 是把堆中对象引用放入 String Table),后续对该字面量或 intern() 结果的访问都指向同一对象
- 这也说明:运行时常量池(尤其 String Table)会随程序运行不断增长,不当使用 intern() 可能引发元空间或堆内存压力
关键区别小结
字面量:编译期确定,加载时入池;其中字符串对象实体在堆,仅引用在运行时常量池(或 String Table)中。
符号引用:编译期生成,加载时原样载入运行时常量池,解析后转为直接引用,不覆盖原符号项。
二者共性:都是类的静态元信息,服务于动态链接、反射、调试等机制;都受方法区内存限制(JDK 8+ 即元空间大小)。

















