两个字符串相加究竟发生了什么
首先从一张图开始,图中对字符串相加和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。