姐夫說
看到群里有人貼這個。Java編譯器真的會把普通的字符串拼接操作“優化”為StringBuilder?有這么糟蹋性能的做法嗎? ????
嗯... 小萌新表示 還好吧. 否則每次在常量空間中創建一個中間值 這樣子多不值當. (雖然的在新的JVM標準中 已經把String 從常量空間挪到新生區域了
這是個嚴重影響性能的做法不是嗎?明明目標字符串長度也可以知道,你搞個SB需要額外字符數組翻幾次倍最后再復制一份,怪不得總是要和GC作斗爭,這么個基礎操作都這么浪費。
原來Java出了二十幾年,.NET出了十五年了,到現在還有那么多人連如何高效地拼接字符串,什么時候該使用StringBuilder什么時候不該使用都不清楚。還有人說一萬個字符串拼接用SB就快了等等,當然不是。SB的使用場景是目標字符串長度不確定的情況,和到底是十個還是十萬個字符串,到底最終長度是一百還是一百萬都沒有關系。比如之前的場景,每個字符串都是確定的,最終長度自然也是確定的,這根本就不應該動用SB。假如你不知道應該怎么做,那只能讓你去看下十五年前開始.NET就采取的做法。在目標字符串長度確定的情況下,出現目標字符串外任意一個額外的內存分配都是不及格。
另外有人說“一個”StringBuilder對象,好像沒多少開銷一樣。但是SB不是一個對象,而是一串對象啊。你append過程中隨時就會分配一個長度翻倍的新的char數組,然后還要復制一遍。多少人以為用SB就夠了一樣,都不知道需要指定一個capacity。
比如那段所謂被Java編譯器“優化”的代碼,capacity也沒指定,翻倍和復制幾乎肯定發生。當然假如他們知道指定capacity,也不會使用SB這么低效的做法了吧。
剛才忘記切換jdk了,java9 已經不是這樣了,會使用makeConcatWithConstants 進行拼接
至于非final時用append來搞,從JDK 1.1時代就是這么玩了。當然,還是寫javac的人偷懶,先確定長度再生成SB會好一些,但是碰到為null時怎么調用xxx.length(),搞到最后變成這樣: 網頁鏈接
正確的字符串拼接方式 網頁鏈接 算出總長,分配目標字符串內存,把輸入的字符串復制到正確的位置。出現任意額外的StringBuilder啊char數組分配什么的都是不及格。 ????
我是真不知道直到 Java 9 才有 StringConcatFactory 這種東西的,所以才沒能理解為什么 Java 程序員普遍覺得除了 .append 就只有 StringBuilder 一途
至于為什么 .NET 這邊不鼓勵濫用 StringBuilder,我覺得就算想一下為什么 Java 9 會有 StringConcatFactory 也該明白了。
多說一句,其實StringBuilder在拼接字符串時也不一定是最優的,因為它其實是把每次Append進去的東西復制展開,因此內存占用是和目標字符串長度相關的。有時候,你拿一個字符串數組/List保留輸入字符串,最后用自己寫的Concat(string[] input, beginIndex, length)拼起來,此時額外的內存占用就是和字符串數量相關,就遠小于目標字符串長度了。而這個臨時字符串數組甚至都可以復用,最終效果便又是零(額外)分配了。當然,這種方法并不是沒有值得討論的地方。一是實際開發時,復用一個巨型的StringBuilder,每線程一個(ThreadStatic),這可能也夠了,開發起來也更方便(效果相對略差)。二是用StringBuilder時,每次Append的字符串可能可以被立即回收,而用上邊描述的方法會導致字符串被長時間引用而被升代(假如它們本身就會被其他地方引用著那么自然就沒這方面問題了)。總之內存優化時很多時候就是圍繞字符串來的,畢竟字符串代表的是一大塊連續內存,而且太容易生成新字符串(Split,ToUpper,Substring等等),操作起來要么浪費要么麻煩。更有甚者是,它很容易/已經被濫用了,例如目標函數就是要接受一個字符串時,你除了生成一個新的就沒其他辦法了。用Span<char>可以解決部分問題,但它一是太新用不上,二是也不能解決所有問題。收起全文
JEP280 為提高性能所做的努力 ????