Java中的JVM字符串常量池详解
字数 812 2025-11-23 09:48:26
Java中的JVM字符串常量池详解
一、字符串常量池的基本概念
字符串常量池(String Pool)是JVM在方法区(Java 8后位于元空间)中开辟的一块特殊内存区域,用于存储字符串常量。其主要目的是减少重复字符串对象的内存开销,通过共享不可变对象来优化性能。例如:
String s1 = "hello";
String s2 = "hello"; // 不会创建新对象,直接引用常量池中的"hello"
此时s1和s2指向同一个内存地址。
二、字符串常量池的底层结构
- Java 7之前:字符串常量池位于永久代(PermGen),容易引发内存溢出。
- Java 7及以后:字符串常量池被移到堆内存中,便于垃圾回收和管理。
- 底层实现:使用哈希表(HashTable) 存储字符串引用,Key为字符串的哈希值,Value为对应String对象的引用。
三、字符串创建的两种方式与常量池的关系
-
直接赋值(字面量):
String s1 = "java"; // 步骤1:检查常量池是否存在"java"- 若存在,直接返回引用;
- 若不存在,在常量池中创建新对象并返回引用。
-
new关键字创建:
String s2 = new String("java"); // 步骤1:与字面量相同,先检查常量池- 常量池中若无"java",则先在常量池创建;
- 然后在堆中创建新String对象,并返回堆中对象的引用。
此时,堆对象与常量池中的对象地址不同,但内容相同。
四、intern()方法的作用
intern()方法用于主动将字符串对象添加到常量池:
- 若常量池中已存在相同内容的字符串,则返回常量池中的引用;
- 若不存在,JDK 6会将对象复制到常量池;JDK 7+ 则直接将堆中对象的引用存入常量池。
String s3 = new String("world").intern(); // 强制使用常量池中的引用
五、内存分配示例分析
String a = "hi"; // 常量池创建"hi"
String b = new String("hi"); // 堆中创建新对象,常量池已存在"hi"
String c = b.intern(); // 返回常量池中"hi"的引用
System.out.println(a == b); // false(地址不同)
System.out.println(a == c); // true(均指向常量池)
六、常量池与垃圾回收
由于字符串常量池位于堆中(Java 7+),其中的对象可以被垃圾回收。当常量池中的字符串不再被任何引用指向时,JVM会在GC时将其回收。
七、性能优化建议
- 优先使用字面量赋值,避免不必要的堆对象创建;
- 在需要重复使用大量相同字符串时,可调用
intern()减少内存占用(但需权衡哈希表操作的开销); - 谨慎在循环中使用
intern(),可能导致哈希冲突性能下降。