字符串池和堆存储

像许多 Java 对象一样,所有 String 实例都是在堆上创建的,甚至是文字。当 JVM 发现在堆中没有等效引用的 String 文字时,JVM 在堆上创建相应的 String 实例,并且它还存储对 String 池中新创建的 String 实例的引用。对同一 String 文字的任何其他引用都将替换为堆中先前创建的 String 实例。

我们来看下面的例子:

class Strings
{
    public static void main (String[] args)
    {
        String a = "alpha";
        String b = "alpha";
        String c = new String("alpha");

        //All three strings are equivalent
        System.out.println(a.equals(b) && b.equals(c));

        //Although only a and b reference the same heap object
        System.out.println(a == b);
        System.out.println(a != c);
        System.out.println(b != c);
    }
}

以上的输出是:

true
true
true
true

StackOverflow 文档 当我们使用双引号创建一个 String 时,它首先在 String 池中查找具有相同值的 String,如果发现它只返回引用,否则它在池中创建一个新 String,然后返回引用。

但是,使用 new 运算符,我们强制 String 类在堆空间中创建一个新的 String 对象。我们可以使用 intern() 方法将它放入池中或从具有相同值的字符串池中引用其他 String 对象。

字符串池本身也在堆上创建。

Version < Java SE 7

在 Java 7 之前,String 文字存储在 PermGen 的方法区域中的运行时常量池中,该区域具有固定大小。

字符串池也位于 PermGen 中。

Version >= Java SE 7

RFC:6962931

在 JDK 7 中,实现的字符串不再分配在 Java 堆的永久生成中,而是分配在 Java 堆的主要部分(称为年轻和旧的代)中,以及应用程序创建的其他对象。此更改将导致更多数据驻留在主 Java 堆中,并且永久生成中的数据更少,因此可能需要调整堆大小。由于这种变化,大多数应用程序只会看到堆使用中的相对较小的差异,但是加载许多类或大量使用 String.intern() 方法的较大应用程序将看到更显着的差异。