字符串池和堆存储
像许多 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
当我们使用双引号创建一个 String 时,它首先在 String 池中查找具有相同值的 String,如果发现它只返回引用,否则它在池中创建一个新 String,然后返回引用。
但是,使用 new 运算符,我们强制 String 类在堆空间中创建一个新的 String 对象。我们可以使用 intern()
方法将它放入池中或从具有相同值的字符串池中引用其他 String 对象。
字符串池本身也在堆上创建。
Version < Java SE 7
在 Java 7 之前,String
文字存储在 PermGen
的方法区域中的运行时常量池中,该区域具有固定大小。
字符串池也位于 PermGen
中。
Version >= Java SE 7
在 JDK 7 中,实现的字符串不再分配在 Java 堆的永久生成中,而是分配在 Java 堆的主要部分(称为年轻和旧的代)中,以及应用程序创建的其他对象。此更改将导致更多数据驻留在主 Java 堆中,并且永久生成中的数据更少,因此可能需要调整堆大小。由于这种变化,大多数应用程序只会看到堆使用中的相对较小的差异,但是加载许多类或大量使用
String.intern()
方法的较大应用程序将看到更显着的差异。