new String()vs literal string performance

StackOverflow上多次询问过这个问题,但没有一个是基于性能的

Effective Java一书中,我们给出了这一点

如果String s = new String("stringette"); 在循环或频繁调用的方法中,可以不必要地创建数百万个String实例。

改进版本只是以下内容: String s = "stringette"; 此版本使用单个String实例,而不是每次执行时都创建一个新实例。

所以,我尝试了两者,发现性能有了显着提高

 for (int j = 0; j < 1000; j++) { String s = new String("hello World"); } 

需要大约399 372纳秒。

 for (int j = 0; j < 1000; j++) { String s = "hello World"; } 

大约需要23 000纳秒。

为什么会有这么多的性能提升? 内部是否有任何编译器优化

在第一种情况下,在每次迭代中创建一个新对象,在第二种情况下,它始终是从String常量池中检索的同一对象。

在Java中,当你这样做时:

 String bla = new String("xpto"); 

您强制创建一个新的String对象,这会占用一些时间和内存。

另一方面,当你这样做时:

 String muchMuchFaster = "xpto"; //String literal! 

String将仅在第一次创建(一个新对象),并且它将被缓存在String常量池中,因此每次以它的文字forms引用它时,您将得到完全相同的对象,这是非常快。

现在你可能会问……如果代码中的两个不同点检索相同的文字并进行更改,那么问题是否会发生?

不,因为您可能非常清楚,Java中的字符串是不可变的! 因此,任何可以改变String的操作都会返回一个新的String,并保留对同一文字的任何其他引用。

这是不可变数据结构的优点之一,但这完全是另一个问题,我会写几篇关于这个主题的内容。

编辑

只是澄清一下,常量池并不是String类型所独有的,你可以在这里阅读更多关于它的信息,或者你是google for Java常量池。

http://docs.oracle.com/javase/specs/jvms/se7/jvms7.pdf

此外,你可以做一点点测试来推动这一点:

 String a = new String("xpto"); String b = new String("xpto"); String c = "xpto"; String d = "xpto"; System.out.println(a == b); System.out.println(a == c); System.out.println(c == d); 

有了这一切,您可以找出这些Sysouts的结果:

 false false true 

由于cd是同一个对象,因此==比较成立。

性能差异实际上要大得多:HotSpot可以轻松编译整个循环

 for (int j = 0; j < 1000; j++) {String s="hello World";} 

不存在所以运行时是一个稳定的0.然而,这只有在JIT编译器启动后才会发生; 这就是热身的目的,这是在JVM上对微软标记任何内容时的强制性过程。

这是我运行的代码:

 public static void timeLiteral() { for (int j = 0; j < 1_000_000_000; j++) {String s="hello World";} } public static void main(String... args) { for (int i = 0; i < 10; i++) { final long start = System.nanoTime(); timeLiteral(); System.out.println((System.nanoTime() - start) / 1000); } } 

这是一个典型的输出:

 1412 38 25 1 1 0 0 1 0 1 

您可以很快观察到JIT的生效。

请注意,我不会在内部方法中迭代一千,而是十亿次。

正如已经回答的那样,第二个从字符串池中检索实例(记住字符串是不可变的)。

另外,你应该检查intern()方法,它允许你将新的String()放入池中,以防你在运行时不知道字符串的常量值:例如:

 String s = stringVar.intern(); 

要么

 new String(stringVar).intern(); 

我将添加其他事实,您应该知道除了String对象之外,池中还存在更多信息(哈希码):这使得能够在相关数据Strtuctures中通过String快速搜索hashMap(而不是每次都重新创建哈希码)

JVM维护一个对文字唯一String对象的引用池。 在新的String示例中,您将使用每个文本的实例包装文本。

请参阅http://www.precisejava.com/javaperf/j2se/StringAndStringBuffer.htm