【问题标题】:How many memory locations will it take to have a string concatenation?字符串连接需要多少个内存位置?
【发布时间】:2013-12-14 05:56:33
【问题描述】:

字符串连接需要多少内存位置?

String myStringVariable = "Hello";

在以下两个语句中:

String s = "ABC" + "Hello" + "DEF";

String s = "ABC";
s = s + "Hello";
s = s + "DEF";

String s = "ABC" + myStringVariable + "DEF";

哪个会消耗更多内存?哪种情况下StringBuilder最有用?

【问题讨论】:

  • 第一个绝对适合常量折叠。第二个应该也是。
  • 更正:第二个例子实际上不会被常量折叠。 (另外:收到答案后您是否必须更改样本?)

标签: java string memory-management stringbuilder


【解决方案1】:

当您想玩字符串时,StringBuilder 很有用。通过组合字符串动态创建字符串。它只会占据一个对象。

如果你每次都使用字符串,任何连接都会在内存中创建新对象。

【讨论】:

  • 简单的字符串连接无论如何都会编译成StringBuilder,所以没有区别。
【解决方案2】:

StringBuilder 在这两种情况下都不会有帮助,正如所写的那样 - 编译器将尽可能重用相同的 StringBuilder 实例。当您在循环中添加字符串时,它略微很有帮助。 (我说“勉强”是因为大多数时候你并没有真正添加 那个 很多字符串,它们也没有 那么 长。差异会出现在微基准测试中,但你的程序不是一个,除非您正在编写模板引擎或类似的东西。)

创建垃圾StringBuilders 不会像造成 GC 压力那样“浪费内存”。事实上,Java 之类的分代清道夫 GC 旨在提高创建大量寿命很短的垃圾对象的效率。除非您实际上在 GC 上花费了太多时间,否则我不会担心。

String 的内容反复复制到StringBuilders 中也是一种浪费,但同样不太可能在循环之外产生重大影响。

【讨论】:

    【解决方案3】:

    第一条语句将被编译器转换为String s = "ABCDEF";,因此不会有连接

    第二条语句将被编译器转换成这段代码(或类似的东西)

        String s = "ABC";
        StringBuilder sb = new StringBuilder(s);
        sb.append("DEF");
        s = sb.toString();
    

    【讨论】:

    • 哪些 JVM 供应商/版本支持此分辨率?特别是第二个(显然至少 >=1.5)?
    • 第一种情况行为由 JLS 定义 - 编译时间常数。第二个 >= 1.5,因为 StringBuilder 是在 1.5 中引入的
    • @Yaneeve:在 1.5 之前,StringBuffer 的使用方式与今天使用 StringBuilder 的方式相同。此功能从第一个版本开始就存在。
    • @Yaneeve:是编译器,即 javac,使用 StringBuilder 创建代码。这与 HotSpot 无关。
    • 看来,在写完这个答案后,这个问题已经被编辑了。该答案所指的“第二个示例”不再存在。当前第二个例子是String s = "ABC" + "Hello" + "DEF";,会编译成String s = "ABCHelloDEF";
    【解决方案4】:
    String s = "ABC" + "DEF";
    

    这在编译时已解决,并且没有 StringBuilder 用于连接。

    String s = "ABC";
    s = s + "DEF";   
    

    在这种情况下StringBuilder(string...).toString() 在运行时解析String

    所以你需要使用StringBuilder,在你的情况下它的性能差异可以忽略不计。

    不要在“+”中使用太多的连接,在较少的连接中不会造成太大的伤害。如果您处理的数量较大,则更喜欢使用带有 append 方法的 StringBuilder。

    如果您在很大程度上将字符串与+ 连接起来,您可以看到区别,然后选择StringBuilder 附加。

    【讨论】:

      【解决方案5】:

      我创建类Main

      public class Main {
          public String foo() {
              String s = "ABC" + "DEF";
              return s;
          }
          public String foo1() {
              String s = "ABC";
              s = s + "DEF";
              return s;
          }
      }
      

      并反编译:

      public java.lang.String foo();
      Signature: ()Ljava/lang/String;
      Code:
         0: ldc           #2                  // String ABCDEF
         2: astore_1
         3: aload_1
         4: areturn
      
      public java.lang.String foo1();
      Signature: ()Ljava/lang/String;
      Code:
         0: ldc           #3                  // String ABC
         2: astore_1
         3: new           #4                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        10: aload_1
        11: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        14: ldc           #7                  // String DEF
        16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: astore_1
        23: aload_1
        24: areturn
      

      方法foo在编译时生成连接字符串,但方法foo1在运行时连接字符串并使用StringBuilder

      第一个示例使用较少的内存。

      我使用具有默认属性的 Oracle JDK 1.7_45。

      【讨论】:

        【解决方案6】:

        哪个会消耗更多内存?

        java规范15.8.1 String Concatenation Operator +:

        String 对象是新创建的(第 12.5 节),除非表达式是 编译时常量表达式 (§15.28)。

        实现可以选择执行转换和连接 一步避免创建然后丢弃中间体 字符串对象。 增加重复字符串的性能 连接,Java 编译器可以使用 StringBuffer 类或 减少中间字符串对象数量的类似技术 通过评估表达式创建的。

        • 第一个陈述,

           String s = "ABC" + "Hello" + "DEF";
          

          这个表达式是一个编译时常量表达式。将一步完成。运行时不会创建任何对象。

        • 第二句话:

            String s = "ABC";
            s = s + "Hello";
            s = s + "DEF";
          

          此语句将创建一个包含"ABCHELLO" 的新字符串。第三条语句将创建一个包含"ABCHELLODEF" 的新字符串。此字符串连接将使用 StringBuilder 进行。

        • 第三句话

          String s = "ABC" + myStringVariable + "DEF";
          

          与第一条语句不同,该语句将在运行时创建一个新实例,因为它不是编译时常量表达式。

        但是使用StringBuilder 连接这三个字符串不会带来性能提升,除非您使用的字符串数量对计数很重要。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多