Javac的StringBuilder优化弊大于利吗?

 手机用户2502863461 发布于 2023-01-05 23:27

假设我们有一些代码如下:

public static void main(String[] args) {
    String s = "";
    for(int i=0 ; i<10000 ; i++) {
        s += "really ";
    }
    s += "long string.";
}

(是的,我知道更好的实现会使用a StringBuilder,但请耐心等待.)

平凡地说,我们可能期望生成的字节码类似于以下内容:

public static void main(java.lang.String[]);
Code:
   0: ldc           #2                  // String 
   2: astore_1      
   3: iconst_0      
   4: istore_2      
   5: iload_2       
   6: sipush        10000
   9: if_icmpge     25
  12: aload_1       
  13: ldc           #3                  // String really 
  15: invokevirtual #4                  // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
  18: astore_1      
  19: iinc          2, 1
  22: goto          5
  25: aload_1       
  26: ldc           #5                  // String long string.
  28: invokevirtual #4                  // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
  31: astore_1      
  32: return

然而,相反,编译器试图变得更聪明 - 而不是使用concat方法,它在优化中使用了StringBuilder对象,因此我们得到以下内容:

public static void main(java.lang.String[]);
Code:
   0: ldc           #2                  // String 
   2: astore_1      
   3: iconst_0      
   4: istore_2      
   5: iload_2       
   6: sipush        10000
   9: if_icmpge     38
  12: new           #3                  // class java/lang/StringBuilder
  15: dup           
  16: invokespecial #4                  // Method java/lang/StringBuilder."":()V
  19: aload_1       
  20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  23: ldc           #6                  // String really 
  25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  28: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  31: astore_1      
  32: iinc          2, 1
  35: goto          5
  38: new           #3                  // class java/lang/StringBuilder
  41: dup           
  42: invokespecial #4                  // Method java/lang/StringBuilder."":()V
  45: aload_1       
  46: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  49: ldc           #8                  // String long string.
  51: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  54: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  57: astore_1      
  58: return

但是,这对我来说似乎反效果 - 而不是为整个循环使用一个字符串构建器,为每个单个连接操作创建一个,使其等效于以下内容:

public static void main(String[] args) {
    String s = "";
    for(int i=0 ; i<10000 ; i++) {
        s = new StringBuilder().append(s).append("really ").toString();
    }
    s = new StringBuilder().append(s).append("long string.").toString();
}

因此,现在不是创建大量字符串对象并抛弃它们的原始琐碎方法,编译器已经产生了一种更糟糕的方法,即创建大量String对象,大量StringBuilder对象,调用更多方法,并且仍然将它们全部丢弃生成与没有此优化时相同的输出.

所以问题必须是 - 为什么?我理解在这种情况下:

String s = getString1() + getString2() + getString3();

...编译器将为StringBuilder所有三个字符串创建一个对象,因此有些情况下优化很有用.但是,检查字节码表明甚至将上述情况分为以下几种:

String s = getString1();
s += getString2();
s += getString3();

...意味着我们回到了StringBuilder单独创建三个对象的情况.我知道这些是不是奇怪的角落情况,但以这种方式(并在循环中)附加到字符串是非常常见的操作.

当然,在编译时确定编译器生成的StringBuilder只是附加了一个值是否是微不足道的- 如果是这种情况,请使用简单的concat操作?

这完全是8u5(然而,它可能会回到至少Java 5,可能之前.)FWIW,我的基准测试(不出所料)使手动concat()方法比使用+=10,000个元素的循环快2到3倍.当然,使用手册StringBuilder始终是首选方法,但编译器肯定不会对+=方法的性能产生负面影响吗?

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有