字符串常量池中的 "a" vs 'new String("a")' vs 'new String("a").intern()' (JDK 6)

"a" vs 'new String("a")' vs 'new String("a").intern()' in String Constant Pool (JDK 6)

以前知道下面两条语句在运行时都会在常量池中创建字符串a

String s = "a"
String s = new String("a")

在 JVM 上测试这些时,两种情况下的 permgen 大小相同。


但是,以下代码片段的行为与此不同:

String s2 = null;
for (int i = 0; i < 100; i++)
{
    s2 = s2 + i;
}

使用.intern(),permgen大小在每次迭代中增加:

String s2 = null;
for (int i = 0; i < 100; i++)
{
    s2 = s2 + i;
    s2.intern();
}

为什么可以观察到这种行为? s2.intern() 是否向池中添加条目?这与这些声明有何不同?

String s = "a"
String s = new String("a")

由于 interned 字符串在 JVM 范围内被 interned,它们必须静态地 interned,这将需要机制使用 class 属性进行缓存,这将消耗 permgen 内存。

这里稍微解释一下:

  • "a" 如果字符串 "a" 不存在,则在实习生池中创建
  • new String("a")因为参数是"a",字符串"a"被创建在实习生池(如果尚未存在)和 "a" 的副本是从实习生池
  • 中创建的
  • 采取任何字符串 s s.intern() returns 如果存在于实习生池中,则该字符串的实习生副本。如果没有将该字符串添加到实习生池和 returns 新副本。

intern()方法参考:

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

注意:创建一个String并且不要将其添加到实习生池你可以使用字符数组作为字符串构造函数的参数:

char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.' };
String helloString = new String(helloArray);

这里是对 jls 的引用,其中解释了字符串文字存在于内部字符串池中:

A string literal is a reference to an instance of class String (§4.3.1, §4.3.3).

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.


这是对最后一条评论的逐步解释:

// Creates the string "123", add it to the intern pool and assign "123" to s
String s = "123";  

// s.intern() returns the intern string of s. Because s is already the 
// string "123" present in the intern pool s and s.intern() are the same
System.out.println(s == s.intern());// true 

// "123" is already in intern pool. new String("123") create a new String
// that equals "123" but it is a different object
String s2 = new String("123"); 

// It prints false because s2 is equals to "123", but not the same object
// because a new String was created in the preciding row
System.out.println(s2 == s);// false

// It prints true because s2.intern() returns the string present in the
// intern pool that is equals to s2. That string exists and is "123"
System.out.println(s2.intern() == s);  // true

附加说明: 对于每个等于 s2 的字符串 s,s.intern() == s2.intern() 也 if s == s2 returns假。

// If s equals s2 but are not the same
if (s.equals(s2) && s != s2) {
    // THe intern string of s and s2 are always the same
    System.out.println(s.intern() == s2.intern()); // prints always true
}