源碼|String拼接操作”+”的優(yōu)化憎瘸?

很多講Java優(yōu)化的文章都會強(qiáng)調(diào)對String拼接的優(yōu)化。倒不用特意記陈瘦,本質(zhì)上在于對不可變類優(yōu)勢和劣勢的理解上幌甘。

需要關(guān)注的是編譯器對String拼接做出的優(yōu)化,在簡單場景下的性能能夠與StringBuilder相當(dāng),復(fù)雜場景下仍然有較大的性能問題锅风。網(wǎng)上關(guān)于這一問題講的非常亂酥诽;如果我講的有什么紕漏,也歡迎指正皱埠。

JDK版本:oracle java 1.8.0_102

本文用到了反編譯工具jad盆均。在查閱網(wǎng)上關(guān)于String拼接操作的優(yōu)化時發(fā)現(xiàn)了這個工具,能同時反編譯出來源碼和字節(jié)碼漱逸,親測好用泪姨,點我下載

String拼接的性能問題

優(yōu)化之前饰抒,每次用”+”拼接肮砾,都會生成一個新的String。特別在循環(huán)拼接字符串的場景下袋坑,性能損失是極其嚴(yán)重的:

  1. 空間浪費(fèi):每次拼接的結(jié)果都需要創(chuàng)建新的不可變類
  2. 時間浪費(fèi):創(chuàng)建的新不可變類需要初始化仗处;產(chǎn)生大量“短命”垃圾,影響 young gc甚至full gc

所謂簡單場景

簡單場景和復(fù)雜場景是我亂起的名字枣宫,幫助理解編譯器的優(yōu)化方案婆誓。

簡單場景可理解為在一句中完成拼接:

int i = 0;
String sentence = “Hello” + “world” + String.valueOf(i) + “\n”;
System.out.println(sentence);

利用jad可看到優(yōu)化結(jié)果:

int i = 0;
String sentence = (new StringBuilder()).append(“Hello”).append(“world”).append(String.valueOf(i)).append(“\n”).toString();
System.out.println(sentence);

是不是很神奇,竟然把String的拼接操作優(yōu)化成了StringBuilder#append()也颤!

此時洋幻,可以認(rèn)為已經(jīng)將簡單場景的空間性能、時間性能優(yōu)化到最優(yōu)(僅針對String拼接操作而言)翅娶,看起來編譯器已經(jīng)完成了必要的優(yōu)化文留。你可以測試一下,簡單場景下的性能能夠與StringBuilder相當(dāng)竭沫。但是——“但是”以前的都是廢話——編譯器的優(yōu)化對于復(fù)雜場景的幫助卻很有限了燥翅。

所謂復(fù)雜場景

所謂復(fù)雜場景,可理解為“編譯器不確定(或很難確定蜕提,于是不做分析)要進(jìn)行多少次字符串拼接后才需要轉(zhuǎn)換回String”森书。可能表述不準(zhǔn)確谎势,理解個大概就好凛膏。

我們分析一個最簡單的復(fù)雜場景:

String sentence = “”;
for (int i = 0; i < 10000000; i++) {
  sentence += “Hello” + “world” + String.valueOf(i) + “\n”;
}
System.out.println(sentence);

理想的優(yōu)化方案

當(dāng)然,無論什么場景它浅,程序猿都可以手動優(yōu)化:

  • 在性能敏感的場景使用StringBuilder完成拼接译柏。
  • 在性能不敏感的場景使用更方便的String镣煮。

PS:別吐槽姐霍,這樣的API設(shè)計是合理的,在合適的地方做合適的事

理想目標(biāo)是把這件事交給javac和JIT:

  • 設(shè)定一個拼接次數(shù)的閾值镊折,超過閾值就啟動優(yōu)化(對于javac有一個編譯期的閾值胯府,JIT有一個運(yùn)行期的閾值,以分階段優(yōu)化)恨胚。
  • 優(yōu)化時骂因,在拼接前生成StringBuilder對象,將拼接操作換成StringBuilder#append()赃泡,繼續(xù)使用該對象寒波,直至“需要”String對象時,使用StringBuilder#toString()“懶加載”新的String對象升熊。

該優(yōu)化方案的難度在于代碼分析:機(jī)器很難知道到底何時“需要”String對象俄烁,所以也很難在合適的位置注入代碼完成“懶加載”。

雖然很難實現(xiàn)级野,但還是給出理想的優(yōu)化結(jié)果页屠,以供實際方案對比:

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);

實際的優(yōu)化方案

利用jad查看實際的優(yōu)化結(jié)果:

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);

可以看到,實際上編譯器的優(yōu)化只能達(dá)到簡單場景的最優(yōu):僅優(yōu)化字符串拼接的一句蓖柔。這種優(yōu)化程度辰企,對于上述復(fù)雜場景的性能提升很有限,循環(huán)時還是會生成大量短命垃圾况鸣,特別是字符串拼接到很大的時候牢贸,空間和時間上都是致命的。

通過對理想方案的分析镐捧,我們也能理解編譯器優(yōu)化的無奈之處:編譯器無法(或很難)通過代碼分析判斷何時是最晚進(jìn)行懶加載的時機(jī)十减。為什么呢?我們將代碼換個形式可能更容易理解:

String sentence = “”;
for (int i = 0; i < 10000000; i++) {
  sentence = sentence + “Hello” + “world” + String.valueOf(i) + “\n”;
}
System.out.println(sentence);

觀察第3行的代碼愤估,等式右側(cè)引用了sentence帮辟。我肉眼知道這句話只完成了字符串拼接,機(jī)器呢玩焰?最起碼由驹,現(xiàn)在的機(jī)器還很難通過代碼判斷。

待以后將人工智能與編譯優(yōu)化結(jié)合起來昔园,就算只能以90%的概率完成優(yōu)化蔓榄,也是非常cool的。

總結(jié)

這個問題我沒有做性能測試默刚。其實也沒必要過于深究甥郑,與其讓編譯器以隱晦的方式完成優(yōu)化,不如用代碼進(jìn)行主動荤西、清晰的優(yōu)化澜搅,讓代碼能夠“自解釋”伍俘。

那么,如果需要優(yōu)化勉躺,使用StringBuilder吧癌瘾。


本文鏈接:源碼|String拼接操作”+”的優(yōu)化?
作者:猴子007
出處:https://monkeysayhi.github.io
本文基于 知識共享署名-相同方式共享 4.0 國際許可協(xié)議發(fā)布饵溅,歡迎轉(zhuǎn)載妨退,演繹或用于商業(yè)目的,但是必須保留本文的署名及鏈接蜕企。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咬荷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子轻掩,更是在濱河造成了極大的恐慌萍丐,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件放典,死亡現(xiàn)場離奇詭異逝变,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奋构,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門壳影,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弥臼,你說我怎么就攤上這事宴咧。” “怎么了径缅?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵掺栅,是天一觀的道長。 經(jīng)常有香客問我纳猪,道長氧卧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任氏堤,我火速辦了婚禮沙绝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鼠锈。我一直安慰自己闪檬,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布购笆。 她就那樣靜靜地躺著粗悯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪同欠。 梳的紋絲不亂的頭發(fā)上样傍,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天横缔,我揣著相機(jī)與錄音,去河邊找鬼铭乾。 笑死剪廉,一個胖子當(dāng)著我的面吹牛娃循,可吹牛的內(nèi)容都是我干的炕檩。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼捌斧,長吁一口氣:“原來是場噩夢啊……” “哼笛质!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起捞蚂,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤妇押,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姓迅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敲霍,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年丁存,在試婚紗的時候發(fā)現(xiàn)自己被綠了肩杈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡解寝,死狀恐怖扩然,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情聋伦,我是刑警寧澤夫偶,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站觉增,受9級特大地震影響兵拢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逾礁,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一卵佛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敞斋,春花似錦截汪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至焰枢,卻和暖如春蚓峦,著一層夾襖步出監(jiān)牢的瞬間舌剂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工暑椰, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留霍转,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓一汽,卻偏偏與公主長得像避消,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子召夹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理岩喷,服務(wù)發(fā)現(xiàn),斷路器监憎,智...
    卡卡羅2017閱讀 134,713評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法纱意,類相關(guān)的語法,內(nèi)部類的語法鲸阔,繼承相關(guān)的語法偷霉,異常的語法,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,312評論 25 707
  • 大雪紛飛褐筛,君行千里类少。 望斷天涯,相思成疾死讹。 輾轉(zhuǎn)反側(cè)瞒滴,憂心難眠。 孤身只影赞警,錦衾更寒妓忍。 明月清輝,披衣對窗愧旦。 問君...
    柳若素閱讀 285評論 0 0
  • 說起《戰(zhàn)狼2》世剖,《戰(zhàn)狼2》開播到今天為止已經(jīng)整整一個禮拜了,輿論還在不斷上升笤虫,《戰(zhàn)狼2》堪比美國大片旁瘫,目前也在中國...
    隨風(fēng)飄蕩閱讀 316評論 0 1