姐夫說
看到群里有人貼這個(gè)搔确。Java編譯器真的會(huì)把普通的字符串拼接操作“優(yōu)化”為StringBuilder字币?有這么糟蹋性能的做法嗎? ????
嗯... 小萌新表示 還好吧. 否則每次在常量空間中創(chuàng)建一個(gè)中間值 這樣子多不值當(dāng). (雖然的在新的JVM標(biāo)準(zhǔn)中 已經(jīng)把String 從常量空間挪到新生區(qū)域了
這是個(gè)嚴(yán)重影響性能的做法不是嗎?明明目標(biāo)字符串長度也可以知道胚委,你搞個(gè)SB需要額外字符數(shù)組翻幾次倍最后再復(fù)制一份课竣,怪不得總是要和GC作斗爭嘉赎,這么個(gè)基礎(chǔ)操作都這么浪費(fèi)置媳。
原來Java出了二十幾年,.NET出了十五年了公条,到現(xiàn)在還有那么多人連如何高效地拼接字符串拇囊,什么時(shí)候該使用StringBuilder什么時(shí)候不該使用都不清楚。還有人說一萬個(gè)字符串拼接用SB就快了等等靶橱,當(dāng)然不是寥袭。SB的使用場(chǎng)景是目標(biāo)字符串長度不確定的情況,和到底是十個(gè)還是十萬個(gè)字符串关霸,到底最終長度是一百還是一百萬都沒有關(guān)系传黄。比如之前的場(chǎng)景,每個(gè)字符串都是確定的队寇,最終長度自然也是確定的膘掰,這根本就不應(yīng)該動(dòng)用SB。假如你不知道應(yīng)該怎么做佳遣,那只能讓你去看下十五年前開始.NET就采取的做法识埋。在目標(biāo)字符串長度確定的情況下,出現(xiàn)目標(biāo)字符串外任意一個(gè)額外的內(nèi)存分配都是不及格零渐。
另外有人說“一個(gè)”StringBuilder對(duì)象惭聂,好像沒多少開銷一樣。但是SB不是一個(gè)對(duì)象相恃,而是一串對(duì)象啊辜纲。你append過程中隨時(shí)就會(huì)分配一個(gè)長度翻倍的新的char數(shù)組,然后還要復(fù)制一遍拦耐。多少人以為用SB就夠了一樣耕腾,都不知道需要指定一個(gè)capacity。
比如那段所謂被Java編譯器“優(yōu)化”的代碼杀糯,capacity也沒指定扫俺,翻倍和復(fù)制幾乎肯定發(fā)生。當(dāng)然假如他們知道指定capacity固翰,也不會(huì)使用SB這么低效的做法了吧狼纬。
剛才忘記切換jdk了,java9 已經(jīng)不是這樣了骂际,會(huì)使用makeConcatWithConstants 進(jìn)行拼接
至于非final時(shí)用append來搞疗琉,從JDK 1.1時(shí)代就是這么玩了。當(dāng)然歉铝,還是寫javac的人偷懶盈简,先確定長度再生成SB會(huì)好一些,但是碰到為null時(shí)怎么調(diào)用xxx.length(),搞到最后變成這樣: 網(wǎng)頁鏈接
正確的字符串拼接方式 網(wǎng)頁鏈接 算出總長柠贤,分配目標(biāo)字符串內(nèi)存香浩,把輸入的字符串復(fù)制到正確的位置。出現(xiàn)任意額外的StringBuilder啊char數(shù)組分配什么的都是不及格臼勉。 ????
我是真不知道直到 Java 9 才有 StringConcatFactory 這種東西的邻吭,所以才沒能理解為什么 Java 程序員普遍覺得除了 .append 就只有 StringBuilder 一途
至于為什么 .NET 這邊不鼓勵(lì)濫用 StringBuilder,我覺得就算想一下為什么 Java 9 會(huì)有 StringConcatFactory 也該明白了宴霸。
多說一句镜盯,其實(shí)StringBuilder在拼接字符串時(shí)也不一定是最優(yōu)的,因?yàn)樗鋵?shí)是把每次Append進(jìn)去的東西復(fù)制展開猖败,因此內(nèi)存占用是和目標(biāo)字符串長度相關(guān)的速缆。有時(shí)候,你拿一個(gè)字符串?dāng)?shù)組/List保留輸入字符串恩闻,最后用自己寫的Concat(string[] input, beginIndex, length)拼起來艺糜,此時(shí)額外的內(nèi)存占用就是和字符串?dāng)?shù)量相關(guān),就遠(yuǎn)小于目標(biāo)字符串長度了幢尚。而這個(gè)臨時(shí)字符串?dāng)?shù)組甚至都可以復(fù)用破停,最終效果便又是零(額外)分配了。當(dāng)然尉剩,這種方法并不是沒有值得討論的地方真慢。一是實(shí)際開發(fā)時(shí),復(fù)用一個(gè)巨型的StringBuilder理茎,每線程一個(gè)(ThreadStatic)黑界,這可能也夠了,開發(fā)起來也更方便(效果相對(duì)略差)皂林。二是用StringBuilder時(shí)朗鸠,每次Append的字符串可能可以被立即回收,而用上邊描述的方法會(huì)導(dǎo)致字符串被長時(shí)間引用而被升代(假如它們本身就會(huì)被其他地方引用著那么自然就沒這方面問題了)础倍≈蛘迹總之內(nèi)存優(yōu)化時(shí)很多時(shí)候就是圍繞字符串來的,畢竟字符串代表的是一大塊連續(xù)內(nèi)存沟启,而且太容易生成新字符串(Split忆家,ToUpper,Substring等等)德迹,操作起來要么浪費(fèi)要么麻煩芽卿。更有甚者是,它很容易/已經(jīng)被濫用了浦辨,例如目標(biāo)函數(shù)就是要接受一個(gè)字符串時(shí)蹬竖,你除了生成一個(gè)新的就沒其他辦法了沼沈。用Span<char>可以解決部分問題流酬,但它一是太新用不上币厕,二是也不能解決所有問題。收起全文
JEP280 為提高性能所做的努力 ????