不少講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 國際許可協議發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名及連接。