不少讲Java优化的文章都会强调对String拼接的优化。倒不用特地记,本质上在于对不可变类优点和劣势的理解上。java
须要关注的是编译器对String拼接作出的优化,在简单场景下的性能可以与StringBuilder至关,复杂场景下仍然有较大的性能问题。网上关于这一问题讲的很是乱;若是我讲的有什么纰漏,也欢迎指正。git
JDK版本:oracle java 1.8.0_102github
本文用到了反编译工具jad。在查阅网上关于String拼接操做的优化时发现了这个工具,能同时反编译出来源码和字节码,亲测好用,点我下载。性能优化
优化以前,每次用”+”拼接,都会生成一个新的String。特别在循环拼接字符串的场景下,性能损失是极其严重的:oracle
简单场景和复杂场景是我乱起的名字,帮助理解编译器的优化方案。app
简单场景可理解为在一句中完成拼接:工具
int i = 0;
String sentence = “Hello” + “world” + String.valueOf(i) + “\n”;
System.out.println(sentence);复制代码
利用jad可看到优化结果:性能
int i = 0;
String sentence = (new StringBuilder()).append(“Hello”).append(“world”).append(String.valueOf(i)).append(“\n”).toString();
System.out.println(sentence);复制代码
是否是很神奇,居然把String的拼接操做优化成了StringBuilder#append()!测试
此时,能够认为已经将简单场景的空间性能、时间性能优化到最优(仅针对String拼接操做而言),看起来编译器已经完成了必要的优化。你能够测试一下,简单场景下的性能可以与StringBuilder至关。可是——“可是”之前的都是废话——编译器的优化对于复杂场景的帮助却颇有限了。优化
所谓复杂场景,可理解为“编译器不肯定(或很难肯定,因而不作分析)要进行多少次字符串拼接后才须要转换回String”。可能表述不许确,理解个大概就好。
咱们分析一个最简单的复杂场景:
String sentence = “”;
for (int i = 0; i < 10000000; i++) {
sentence += “Hello” + “world” + String.valueOf(i) + “\n”;
}
System.out.println(sentence);复制代码
固然,不管什么场景,程序猿均可以手动优化:
PS:别吐槽,这样的API设计是合理的,在合适的地方作合适的事。
理想目标是把这件事交给javac和JIT:
该优化方案的难度在于代码分析:机器很难知道到底什么时候“须要”String对象,因此也很难在合适的位置注入代码完成“懒加载”。
虽然很难实现,但仍是给出理想的优化结果,以供实际方案对比:
String sentence = “”;
StringBuilder sentenceSB = new StringBuilder(sentence);
for (int i = 0; i < 10000000; i++) {
sentenceSB.append(“Hello”).append(“world”).append(String.valueOf(i)).append(“\n”);
}
sentence = sentenceSB.toString();
System.out.println(sentence);复制代码
利用jad查看实际的优化结果:
String sentence = “”;
for (int i = 0; i < 10000000; i++) {
sentence = (new StringBuilder()).append(sentence).append(“Hello”).append(“world”).append(String.valueOf(i)).append(“\n”).toString();
}
System.out.println(sentence);复制代码
能够看到,实际上编译器的优化只能达到简单场景的最优:仅优化字符串拼接的一句。这种优化程度,对于上述复杂场景的性能提高颇有限,循环时仍是会生成大量短命垃圾,特别是字符串拼接到很大的时候,空间和时间上都是致命的。
经过对理想方案的分析,咱们也能理解编译器优化的无奈之处:编译器没法(或很难)经过代码分析判断什么时候是最晚进行懒加载的时机。为何呢?咱们将代码换个形式可能更容易理解:
String sentence = “”;
for (int i = 0; i < 10000000; i++) {
sentence = sentence + “Hello” + “world” + String.valueOf(i) + “\n”;
}
System.out.println(sentence);复制代码
观察第3行的代码,等式右侧引用了sentence。我肉眼知道这句话只完成了字符串拼接,机器呢?最起码,如今的机器还很难经过代码判断。
待之后将人工智能与编译优化结合起来,就算只能以90%的几率完成优化,也是很是cool的。
这个问题我没有作性能测试。其实也不必过于深究,与其让编译器以隐晦的方式完成优化,不如用代码进行主动、清晰的优化,让代码可以“自解释”。
那么,若是须要优化,使用StringBuilder吧。
本文连接:源码|String拼接操做”+”的优化?
做者:猴子007
出处:monkeysayhi.github.io
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布,欢迎转载,演绎或用于商业目的,可是必须保留本文的署名及连接。