Skip to content

字符串常量池

字符串常量池是 JVM 中的一块特殊内存区域,专门用于存储字符串字面量。相同内容的字符串字面量只会存储一份,多个引用共享同一个对象。

存储位置演变

JDK 版本存储位置
JDK 7 之前方法区(永久代)
JDK 7+

JDK 7 把常量池移入堆,解决了永久代空间不足的问题。

入池规则

创建方式是否入池
字面量 "hello"
new String("hello")
intern()

基本原理

java
public class ConstantPoolDemo {

    public static void main(String[] args) {
        // 字面量:直接使用常量池
        String s1 = "Hello";
        String s2 = "Hello";
        System.out.println("s1 == s2: " + (s1 == s2));  // true

        // new String:不入池,堆中创建新对象
        String s3 = new String("Hello");
        System.out.println("s1 == s3: " + (s1 == s3));  // false

        // intern():手动入池
        String s4 = s3.intern();
        System.out.println("s1 == s4: " + (s1 == s4));  // true
    }
}

s1 == s2 为 true,是因为字面量 "Hello" 被放入常量池后,两个变量引用了同一个对象。

s1 == s3 为 false,是因为 new String() 会在堆中单独创建一个对象。

intern() 方法

intern() 方法会将字符串放入常量池,并返回池中的引用。

java
public class InternDemo {

    public static void main(String[] args) {
        // 编译时常量拼接,结果入池
        String s1 = "Hello" + "World";
        String s2 = "HelloWorld";
        System.out.println("s1 == s2: " + (s1 == s2));  // true

        // 变量拼接,运行时计算,不入池
        String a = "Hello";
        String b = a + "World";  // 实际是 new StringBuilder().append(a).append("World").toString()
        String c = "HelloWorld";
        System.out.println("b == c: " + (b == c));  // false

        // b.intern() 后入池
        String d = (a + "World").intern();
        System.out.println("d == c: " + (d == c));  // true
    }
}

编译器会对字面量拼接做优化,直接变成 "HelloWorld",所以 s1 == s2 为 true。但变量拼接是在运行时通过 StringBuilder 完成的,生成的字符串不入池。

内存优化

java
public class MemoryOptimizationDemo {

    public static void main(String[] args) {
        // 推荐:复用常量池
        String s1 = "config";
        String s2 = "config";

        // 不推荐:创建多个堆对象
        String s3 = new String("config");

        // 大量相似字符串时 intern() 可节省内存
        String[] keys = {"key1", "key2", "key3"};
        String[] optimized = new String[keys.length];
        for (int i = 0; i < keys.length; i++) {
            optimized[i] = keys[i].intern();
        }
    }
}

在大量重复字符串的场景下,合理使用 intern() 可以显著降低内存占用。

基于 VitePress 构建