使用+分配单个字符串值的性能成本是多少

时间:2009-03-02 09:56:37

标签: c# java performance string compiler-optimization

我经常想知道这一点,在最初为字符串赋值时,是否存在将字符串拆分为多行以提高可读性的性能成本。我知道字符串是不可变的,因此每次都需要创建一个新的字符串。此外,由于今天真正快速的硬件(除非你处于一些恶魔般的循环中),性能成本实际上是无关紧要的。例如:

String newString = "This is a really long long long long long" +
    " long long long long long long long long long long long long " +
    " long long long long long long long long long string for example.";

JVM或.Net的编译器和其他优化如何处理这个问题。它会创建一个字符串吗?或者它会创建一个字符串,然后一个新的连接值,然后另一个连接值再次?

这是出于我自己的好奇心。

8 个答案:

答案 0 :(得分:28)

这可以保证C#规范与在单个文字中创建字符串相同,因为它是一个编译时常量。从C#3规范的第7.18节开始:

  

每当表达式满足时   上面列出的要求,   表达式在   编译时间。即使是这样也是如此   表达式是a的子表达式   包含更大的表达式   非常数构造。

(有关“上述要求”的确切详情,请参阅规格:)

Java语言规范在section 3.10.5

的底部附近指定它
  

由常量计算的字符串   表达式(第15.28节)的计算公式为   编译时间然后如同处理   他们是文字。

答案 1 :(得分:14)

实际上,在Java中,编译器会将String转换为常量。

class LongLongString
{
    public LongLongString()
    {
        String newString = "This is a really long long long long long" +
            " long long long long long long long long long long long long " +
            " long long long long long long long long long string for example.";
    }

    public static void main(String[] args)
    {
        new LongLongString();
    }
}

编译成:

Compiled from "LongLongString.java"
class LongLongString extends java.lang.Object{
public LongLongString();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   ldc #2; //String This is a really long long long long long long long long long long long long long long long long long  long long long long long long long long long string for example.
   6:   astore_1
   7:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #3; //class LongLongString
   3:   dup
   4:   invokespecial   #4; //Method "<init>":()V
   7:   pop
   8:   return

}

可以看出,第4行加载了一行,而不是加载了多个String个实例。

编辑:源文件是使用javac版本1.6.0_06编译的。查看The Java Language Specification, Third Edition,(以及Jon Skeet's answer中提到的相同部分),我无法找到有关编译器是否应将多行String连接成单个{{}的任何参考。 1}},所以这种行为可能是特定于编译器实现的。

答案 2 :(得分:6)

自己测试一下。在C#代码中(等效的Java也可以):

string x = "A" + "B" + "C";
string y = "ABC";

bool same = object.ReferenceEquals(x, y); // true

您会看到结果为true

另外,您将看到该字符串也在运行时的字符串池中实现:

bool interned = object.ReferenceEquals(x, string.Intern(x)); // true

答案 3 :(得分:5)

没有性能权衡。编译器的优化会将其合并为一个字符串(至少在Java中)。

答案 4 :(得分:3)

据我所知,这不会创建多个字符串,只有一个字符串。

答案 5 :(得分:3)

等效的.NET IL来补充coobird's answer

对于C#代码:

string s = "This is a really long long long long long" +
    " long long long long long long long long long long long long " +
    " long long long long long long long long long string for example.";
Console.WriteLine(s);

调试编译产生:

.method public hidebysig static void Main(string[] args) cil managed
{
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
  .maxstack 1
  .locals init (
      [0] string str)
  L_0000: ldstr "This is a really long long long long long long long long long long long long long long long long long  long long long long long long long long long string for example."
  L_0005: stloc.0 
  L_0006: ldloc.0 
  L_0007: call void [mscorlib]System.Console::WriteLine(string)
  L_000c: ret 
}

所以,正如你所看到的,它是一个字符串。

答案 6 :(得分:2)

只要所有字符串都是常量(就像在你的例子中一样),在Java中(我想象C#),编译器将其转换为单个字符串。

如果连接了很多动态字符串,例如在循环中,你只会遇到+的性能问题。在这种情况下,请使用StringBuilder或StringBuffer。

答案 7 :(得分:0)

免责声明:这适用于Java。我认为它适用于c#

javac不仅会创建一个String,而且JVM会对包含相同文本的所有其他String使用一个String。

String a = "He" + "llo th"+ "ere";
String b = "Hell" + "o the"+ "re";
String c = "Hello" +" "+"there";
assert a == b; // these are the same String object.
assert a == c; // these are the same String object.

注意:它们在运行时将是相同的String对象,即使它们位于不同JARS中的不同类中,由不同的编译器编译。