【问题标题】:What happens at compile and runtime when concatenating an empty string in Java?在 Java 中连接空字符串时,在编译和运行时会发生什么?
【发布时间】:2010-11-14 11:19:26
【问题描述】:

这个问题主要是出于好奇(并消磨了一些时间)。为了具体起见,我专门询问 Java。

如果我将一个字符串(任何字符串)与一个空字符串连接起来,在内存中会发生什么,例如:

String s = "any old string";
s += "";

我知道之后,s 的内容仍然是“任何旧字符串”,因为空的 ASCII 字符串作为 ASCII 空值存储在内存中(因为至少在 Java 中,字符串总是以空值结尾) .但是我很想知道 Java(编译器?VM?)是否执行了足够的优化以知道 s 将保持不变,并且它可以完全忽略字节码中的该指令,或者在编译和运行时是否发生了不同的事情。

【问题讨论】:

    标签: java string optimization


    【解决方案1】:

    字节码时间到了!

    class EmptyString {
        public static void main(String[] args) {
            String s = "any old string";
            s += "";
        }
    }
    

    javap -c EmptyString:

    编译自“EmptyString.java” 类 EmptyString 扩展 java.lang.Object{ 空字符串(); 代码: 0:aload_0 1:调用特殊#1; //方法 java/lang/Object."":()V 4:返回 公共静态无效主(java.lang.String[]); 代码: 0:最不发达国家#2; //字符串任何旧字符串 2:astore_1 3:新#3; //类 java/lang/StringBuilder 6:重复 7:调用特殊#4; //方法 java/lang/StringBuilder."":()V 10:aload_1 11:调用虚拟#5; //方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14:最不发达国家#6; //细绳 16:调用虚拟#5; //方法java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19:调用虚拟#7; //方法 java/lang/StringBuilder.toString:()Ljava/lang/String; 22:astore_1 23:返回 }

    您可以看到+= 会导致创建StringBuilder,无论它连接什么,因此无法在运行时对其进行优化。

    另一方面,如果将两个字符串文字放在同一个表达式中,它们会被编译器连接起来:

    class EmptyString {
        public static void main(String[] args) {
            String s = "any old string" + "";
        }
    }
    

    javap -c EmptyString:

    编译自“EmptyString.java” 类 EmptyString 扩展 java.lang.Object{ 空字符串(); 代码: 0:aload_0 1:调用特殊#1; //方法 java/lang/Object."":()V 4:返回 公共静态无效主(java.lang.String[]); 代码: 0:最不发达国家#2; //字符串任何旧字符串 2:astore_1 3:返回 }

    【讨论】:

    • @mmyers:您应该指出 1) 发出的字节码是(理论上)Java 编译器特定的,2)JIT 编译器(理论上)可以进一步优化。
    • 是的,JLS 确实为此提供了一些余地 (java.sun.com/docs/books/jls/third_edition/html/…)。
    【解决方案2】:

    执行该行后你会得到一个新的字符串

    s += "";
    

    Java 分配一个新的 String 对象,并在字符串连接后将其分配给 s。如果你有 eclipse 方便(我假设你可以在 NetBeans 中做同样的事情,但我只使用过 eclipse)你可以断点该行并观察 s 指向的对象的对象 ID在执行该行之前和之后。在我的例子中,s 的对象 ID 在该行代码之前是 id=20,之后是 id=24。

    【讨论】:

      猜你喜欢
      • 2010-11-20
      • 1970-01-01
      • 2021-05-23
      • 2016-11-11
      • 1970-01-01
      • 2021-11-18
      • 2017-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多