运行时常量池vs字符串常量池
运行时常量池vs字符串常量池
- 运行时常量池(Runtime Constant Pool):类加载后生成,属于类级别的常量池
- 字符串常量池(String Pool / StringTable):专门存储字符串的全局共享池
可以理解为:
字符串常量池是运行时常量池的一部分演化出来的“特殊优化区”
一、运行时常量池
1.1 定义
运行时常量池是 .class 文件中 常量池(Constant Pool) 在运行时的表现形式。
1.2 存储内容
包括:
- 字面量(Literal)
- 数值(int、float…)
- 字符串(早期直接在这里)
- 符号引用(Symbolic Reference)
- 类名
- 方法名
- 字段名
1.3 特点
- 每个类都有一个
- 在类加载时创建
- 支持动态添加(比如
String.intern())
1.4 存储位置(重点)
- JDK7 之前:方法区(Method Area / PermGen)
- JDK8 之后:方法区实现为 元空间(Metaspace)
二、字符串常量池(String Pool)
2.1 定义
专门用于存储 字符串对象引用 的池
2.2 特点
- 全局唯一(所有类共享)
- 用于字符串复用(避免重复创建)
- 本质是一个 HashTable(StringTable)
2.3 存储位置(重点)
- JDK6:方法区(PermGen)
- JDK7+:堆(Heap) (面试重点)
这也是你看到“静态变量/字符串在堆中”的原因之一
三、关键区别对比
| 维度 | 运行时常量池 | 字符串常量池 |
|---|---|---|
| 范围 | 每个类独立 | 全局共享 |
| 内容 | 各种常量 + 符号引用 | 只存字符串 |
| 位置(JDK8) | 元空间 | 堆 |
| 创建时机 | 类加载时 | 按需创建 |
| 是否唯一 | ❌ | ✅ |
| 是否可扩展 | ✅(运行时) | ✅(intern) |
四、两者关系(重点理解)
4.1 编译期
1 | String s = "abc"; |
"abc"→ 放入 class 常量池- 类加载 → 进入 运行时常量池
- 同时
"abc"→ 进入 字符串常量池
4.2 运行期
1 | String s1 = new String("abc"); |
过程:
"abc"已存在于字符串常量池new String()→ 在堆中创建新对象s1指向堆对象
4.3 intern() 行为
1 | String s2 = new String("abc").intern(); |
- 如果字符串池中不存在 → 放进去
- 返回池中的引用
五、经典面试题(必须会)
❓1
1 | String s1 = "abc"; |
✅ true
👉 因为都指向字符串常量池同一个对象
❓2
1 | String s1 = new String("abc"); |
❌ false
👉 一个在堆,一个在字符串池
❓3(高频)
1 | String s1 = new String("a") + new String("b"); |
👉 JDK7+ 结果:
1 | true |
原因:
"ab"首次出现intern()直接把堆中对象放进字符串池
六、总结(面试模板)
可以这样回答:
运行时常量池是类加载后生成的常量池,属于每个类独立的数据结构,存储字面量和符号引用;而字符串常量池是 JVM 专门优化字符串复用设计的全局共享池,只存储字符串对象引用。在 JDK7 之后,字符串常量池从方法区迁移到了堆中,两者在实现和作用上是不同层次的结构。