Java 中的字符串常量池和运行时常量池
Java 中的字符串常量池和运行时常量池
1. 字符串常量池(String Constant Pool)
定义
字符串常量池是 JVM 内存中专门用于存储字符串字面量和通过 intern()
方法加入的字符串对象的区域。它的目的是避免重复创建相同的字符串对象,以节省内存,并提高性能。
如何工作
- 每当遇到一个字符串字面量(如
"Hello"
),JVM 会首先检查这个字符串是否已经存在于字符串常量池中。如果已存在,它就返回该字符串的引用;如果不存在,它就将这个字符串加入常量池,并返回该字符串的引用。 - 由于字符串是不可变的,JVM 通过这种方式共享字符串对象,避免了大量相同内容字符串的冗余存储。
示例
public class StringConstantPool {
public static void main(String[] args) {
String str1 = "Hello"; // 字符串字面量
String str2 = "Hello"; // 字符串字面量
System.out.println(str1 == str2); // 输出 true
}
}
解释
- str1 和 str2 都是 "Hello" 字符串字面量,在字符串常量池中只有一个 "Hello" 字符串对象,因此它们指向同一个内存地址。str1 == str2 返回 true,说明它们是同一个对象。
使用 intern() 方法
- 如果字符串是通过 new 关键字创建的,它并不会直接进入字符串常量池,而是新创建一个对象。但你可以通过 intern() 方法显式将其添加到常量池中。
示例
public class StringInternExample {
public static void main(String[] args) {
String str1 = new String("Hello");
String str2 = str1.intern(); // 将字符串 "Hello" 添加到常量池
String str3 = "Hello"; // 直接从常量池中获取 "Hello"
System.out.println(str1 == str2); // 输出 false,str1 是通过 new 创建的
System.out.println(str2 == str3); // 输出 true,str2 和 str3 都指向常量池中的 "Hello"
}
}
解释
- str1 是通过 new 创建的,因此它指向堆内存中的对象,而不是字符串常量池中的对象。
- str2 调用了 intern(),它将 "Hello" 加入常量池,并返回池中的引用,因此 str2 和 str3 指向相同的对象。
1. 运行时常量池(Runtime Constant Pool)
定义:
每个 .class 文件中都包含了一个常量池,它存储了类中的各种符号引用,包括类名、字段名、方法名、接口名等。类加载时,JVM 会解析这些符号引用,将其转化为实际的内存地址(直接引用)。
如何工作:
- 常量池包含:常量池不仅包含字符串常量,还包括类、字段、方法等符号引用。
- 在类加载时,常量池中的符号引用会被替换为直接引用,这些直接引用指向内存中的具体对象或方法等。
示例:
public class RuntimeConstantPool {
public static final String CONSTANT = "Hello, World"; // 存储在运行时常量池
public static void main(String[] args) {
System.out.println(CONSTANT); // 直接使用运行时常量池中的常量
}
}
解释
- CONSTANT 被标记为 final,它是一个编译时常量,并会被存储在类的运行时常量池中。
- 当程序执行时,常量 CONSTANT 会被直接引用,并打印出 "Hello, World"。
常量池中符号引用的解析
- 符号引用:指的是在类文件中记录的引用,如方法名、字段名等。
- 解析过程:当类加载时,JVM 会将这些符号引用解析为实际的内存地址,称为直接引用。
示例
public class SymbolicReferenceExample {
public static void main(String[] args) {
Class<?> clazz = String.class; // 符号引用:类名
System.out.println(clazz.getName()); // 解析:打印类名
}
}
解释
- clazz 是一个符号引用,表示 String.class 类,JVM 在类加载过程中会解析这个符号引用,并将其转化为直接引用,从而获得类的内存地址。
3. 字符串常量池与运行时常量池的关系
- 字符串常量池是运行时常量池的一部分,专门存储字符串字面量。在类加载过程中,所有的字符串字面量都会被存储在字符串常量池中,并可以共享使用。
- 运行时常量池包含了更多类型的常量,不仅仅是字符串,还包括其他类、方法、字段等的符号引用。
public class ConstantPoolExample {
public static final String STR_CONSTANT = "Hello";
public static final int NUM_CONSTANT = 100;
public static void main(String[] args) {
System.out.println(STR_CONSTANT); // 来自字符串常量池
System.out.println(NUM_CONSTANT); // 来自运行时常量池
}
}
解释
- STR_CONSTANT 存储在字符串常量池中。
- NUM_CONSTANT 存储在类的运行时常量池中。
- 两者都在类的加载过程中被解析,分别对应各自的常量池区域。
总结
- 字符串常量池:只用于存储字符串字面量和 intern() 添加的字符串对象,避免重复创建相同的字符串对象,提高内存使用效率。
- 运行时常量池:存储类中的各种常量和符号引用,包含了字符串字面量、类名、字段名、方法名等。在类加载时,JVM 将这些符号引用解析为直接引用。
- 优化内存和性能:通过共享常量池中的常量,JVM 避免了大量重复的对象创建,从而节省内存,提升了性能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
2022-12-10 1691. 堆叠长方体的最大高度