首页
登录 | 注册

两个字符串相加究竟发生了什么

首先从一张图开始,图中对字符串相加和StringBuild.append()做了性能对比。

两个字符串相加究竟发生了什么

有人说字符串每次相加都新建了一个对象所以慢,事情真的是这样吗?

先要了解真相最好的方法当然是反编译了,使用 javap -c 反编译的结果

public void test();
    Code:
       0: ldc           #2                  // String s1
       2: astore_1
       3: ldc           #3                  // String s2
       5: astore_2
       6: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
       9: lstore_3
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/String.length:()I
      14: ldc           #6                  // int 100000
      16: if_icmpge     41
    
    //虽然是字符串相加但是仍然创建了StringBuilder对象
      19: new           #7                  // class java/lang/StringBuilder
      22: dup
      23: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
      26: aload_1
      27: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      30: aload_2
      31: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    //将拼接好的字符串重新复制给s1的时候 toString
      34: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    //赋值给s1
      37: astore_1
    回到10,循环 继续创建新的StringBuilder。然后toString
      38: goto          10
      41: getstatic     #11                 // Field java/lang/System.out:Ljava/io/PrintStream;
      44: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
      47: lload_3
      48: lsub
      49: invokevirtual #12                 // Method java/io/PrintStream.println:(J)V



    // 下面是SringBuilder
      52: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
      55: lstore        5
    // 生成一个StringBuilder 57: new #7 // class java/lang/StringBuilder 60: dup 61: ldc #2 // String s1 63: invokespecial #13 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 66: astore 7 68: aload 7 70: invokevirtual #14 // Method java/lang/StringBuilder.length:()I 73: ldc #6 // int 100000 75: if_icmpge 88 78: aload 7 80: aload_2     //调用append方法,不新建对象

    81: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 84: pop 85: goto 68 88: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream; 91: invokestatic #4 // Method java/lang/System.currentTimeMillis:()J 94: lload 5 96: lsub 97: invokevirtual #12 // Method java/io/PrintStream.println:(J)V 100: return

 

 

 

这个时候你可能有疑问,这个循环执行了50000次,创建了50000个StringBuilder对象会慢这么多吗?

其实StringBuilder的toString方法会调用new String()也就是说会创建十万个对象,并且量变会引起质变,

十万个对象产生的耗时不仅是创建对象的耗时,还有gc的耗时,下面是gc日志

[GC (Allocation Failure) [PSYoungGen: 33280K->1576K(38400K)] 33280K->1584K(125952K), 0.0262659 secs] [Times: user=0.03 sys=0.02, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 34856K->1224K(71680K)] 34864K->1240K(159232K), 0.0019681 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 67784K->1158K(71680K)] 67800K->1174K(159232K), 0.0094555 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 67718K->1118K(138240K)] 67734K->1134K(225792K), 0.0287607 secs] [Times: user=0.06 sys=0.00, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 134238K->1155K(138240K)] 134254K->1171K(225792K), 0.0214991 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 134275K->1109K(268288K)] 134291K->1125K(355840K), 0.0133364 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 267349K->69K(268288K)] 267365K->1163K(355840K), 0.0325131 secs] [Times: user=0.02 sys=0.02, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 266309K->119K(534528K)] 267403K->1213K(622080K), 0.0109569 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 532599K->86K(534528K)] 533693K->1180K(622080K), 0.0294418 secs] [Times: user=0.00 sys=0.02, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 532566K->221K(689152K)] 533660K->1315K(776704K), 0.0138167 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 687325K->177K(689152K)] 688419K->1271K(776704K), 0.0188002 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 687281K->113K(689152K)] 688375K->1207K(776704K), 0.0177870 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 687217K->121K(689152K)] 688311K->1215K(776704K), 0.0004647 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 687225K->128K(689152K)] 688319K->1222K(776704K), 0.0187945 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 687232K->238K(689152K)] 688326K->1332K(776704K), 0.0005238 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 687342K->360K(689152K)] 688436K->1454K(776704K), 0.0004996 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 687464K->262K(689152K)] 688558K->1356K(776704K), 0.0010731 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 687366K->395K(688128K)] 688460K->1489K(775680K), 0.0014061 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 687499K->158K(688640K)] 688593K->1252K(776192K), 0.0160660 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 687262K->427K(689664K)] 688356K->1520K(777216K), 0.0006749 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 688555K->305K(689664K)] 689648K->1398K(777216K), 0.0026644 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 688433K->314K(689664K)] 689526K->1408K(777216K), 0.0006023 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 688442K->178K(689664K)] 689536K->1271K(777216K), 0.0143623 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 688306K->333K(689664K)] 689399K->1426K(777216K), 0.0022489 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 688461K->341K(689664K)] 689554K->1435K(777216K), 0.0230670 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 688469K->350K(688640K)] 689563K->1444K(776192K), 0.0205516 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 688478K->195K(689152K)] 689572K->1289K(776704K), 0.0035319 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 688323K->366K(690176K)] 689417K->1460K(777728K), 0.0115201 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 689518K->546K(690176K)] 690612K->1639K(777728K), 0.0176273 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 689698K->557K(690176K)] 690791K->1651K(777728K), 0.0018610 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 689709K->210K(690176K)] 690803K->1304K(777728K), 0.0134489 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 689362K->580K(690176K)] 690456K->1673K(777728K), 0.0004401 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 689732K->218K(690176K)] 690825K->1311K(777728K), 0.0015334 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 689370K->411K(690176K)] 690463K->1505K(777728K), 0.0006384 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 689563K->611K(690176K)] 690657K->1705K(777728K), 0.0005230 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

  

总结:

两个字符串相加底层会生成StringBuilder对象,然后使用append来拼接,如果不在循环中使用,可以不用StringBuilder,

但是养成使用StringBuilder的习惯能避免出现问题,如果使用idea的话,万能的alt+enter会立刻把字符串相加变成

StringBuilder。


相关文章

  • 从 docker 到 runC
    笔者在前文<RunC 简介>和<Containerd 简介>中分别介绍了 runC 和 containerd.本文我们将结合 docker 中的其它组件探索 docker 是如何把这些组件组织起来协调工作的. Doc ...
  • 如何在电脑上配置两个tomcat
    问题 准备逐渐转向idea的怀抱了,每次部署项目时和eclipse使用的都是同一个tomcat,这是很大的隐患,并且非常的不方便,遂再配置一个tomcat 1.下载tomcat和配置系统变量 CATALINA_HOME是Tomcat的安装目 ...
  • .NET Core Dapper操作mysql数据库
    前言 现在ORM盛行,市面上已经出现了N款不同的ORM套餐了.今天,我们不谈EF,也不聊神马黑马,就说说 Dapper.如何在.NET Core中使用Dapper操作Mysql数据库呢,让我们跟随镜头(手动下翻)一看究竟. 配置篇 俗话说得 ...
  • 一.前言 在日常开发中,我们经常会碰到需要在运行时才知道对象个数的情况,这种情况不能使用数组,因为数组是固定数量的,这个时候我们就会使用集合,因为集合可以存储数量不确定的对象. 集合类是特别有用的工具类,不仅可以存储数量不等的对象,还可以实 ...
  • More Effective C++
    More Effective C++ 35个改善编程与设计的有效方法 只有深入了解C++编译器如何解释代码, 才有可能用C++语言写出健壮的软件. C++的难学, 不仅在其广博的语法, 语法背后的语义, 语义背后的深层思维, 深层思维背后的 ...
  • MongoDB【快速入门】
    1.MongDB 简介 MongoDB(来自于英文单词"Humongous",中文含义为"庞大")是可以应用于各种规模的企业.各个行业以及各类应用程序的开源数据库.作为一个适用于敏捷开发的数据库,Mo ...

2019 cecdns.com webmaster#cecdns.com
12 q. 0.077 s.
京ICP备10005923号