提升 Java 應用程序的十個優(yōu)化技巧
程序性能優(yōu)化是一個復雜的話題。往往需要結合具體場景進行性能分析,找出瓶頸提出優(yōu)化建議。但是,假設我們平時很少關注細節(jié)的性能,那么這種情況下,優(yōu)化這些細節(jié)所帶來的收益也是相當可觀的。接下來,我們就來說說Java代碼細節(jié)優(yōu)化的一些小技巧。

?復雜的字符串連接操作使用 StringBuilder
職業(yè)生涯早期,在做字符串連接操作的時候,肯定會這么寫:String a=c+e+d,這個Java語法糖對于開發(fā)者來說太方便了。但是如果你在循環(huán)中使用“+”,那就得小心了。
我們都知道String 是不可變的,因此循環(huán)中對 string 的每一次賦值都會在堆內(nèi)存中創(chuàng)建一個新的 String 對象。在一個循環(huán)體中,反復創(chuàng)建多個無用的對象,不僅會占用內(nèi)存空間,還會影響GC時間。所以說,如果在循環(huán)中遇到字符串拼接,就使用 StringBuilder 而不是“+”。
使用 ThreadPoolExecutor 避免手動創(chuàng)建線程
許多初學者喜歡在編寫代碼時創(chuàng)建線程,這是一種危險的做法。
如果這個線程的創(chuàng)建需要處理大量的請求,很可能導致你的程序頻繁的創(chuàng)建和銷毀線程,頻繁的切換線程上下文,浪費CPU資源,甚至會耗盡內(nèi)存。
因此,建議使用ThreadPoolExecutor,并配置合適的核心線程數(shù)和最大線程數(shù)。
為集合預分配適當?shù)娜萘?/h4>
我們都知道 ArrayList,HashMap 和 ConcurrentHashMap 等集合類是可以自動擴容的,但是這種自動擴容涉及到底層數(shù)組的復制和遷移。如果擴容頻繁,肯定會影響程序的性能。所以如果你能估計出大概的容量,請直接配置初始值。
使用枚舉而不是常量類
很多人特別喜歡在項目中創(chuàng)建一個常量類,如下:
為什么不用枚舉呢?Enum 有強制的類型驗證。同時,使用枚舉類的性能更高。并且使用 enum 還有更大的優(yōu)勢,它可以與策略模式一起使用來提高程序的可擴展性。例如:
如代碼所示,你可以根據(jù)需要動態(tài)選擇一種策略來下載文件,直接調用FileType.EXCEL.download(),無需關心代碼細節(jié)。
使用 NIO 代替?zhèn)鹘y(tǒng) IO
傳統(tǒng)的 IO 已經(jīng)過時了。強烈推薦使用 NIO 代替?zhèn)鹘y(tǒng)的 IO。因為傳統(tǒng)IO采用阻塞IO模型,請求數(shù)據(jù)后,線程從數(shù)據(jù)準備到數(shù)據(jù)可讀都是阻塞的。
而且,傳統(tǒng)IO如果要往網(wǎng)卡寫數(shù)據(jù),需要先把數(shù)據(jù)寫到堆內(nèi)存,然后再把數(shù)據(jù)拷貝到堆外的一塊內(nèi)存,再從用戶態(tài)拷貝數(shù)據(jù)到內(nèi)核狀態(tài)緩沖區(qū)。最后CPU通知DMA將數(shù)據(jù)寫入網(wǎng)卡,一共經(jīng)歷了3次拷貝。NiO不僅采用了multiplex IO模型,還可以使用direct memory來減少數(shù)據(jù)拷貝次數(shù),從而提高性能。
使用移位操作
如果你看過一些JDK的源代碼,比如HashMap,你會發(fā)現(xiàn)代碼中有很多移位操作。因為JDK是比較底層的代碼,對性能的追求也是極致的。在我們?nèi)粘5木幋a中,可以用移位運算來代替一些乘除運算,比如a >> 1 代替 a / 2,a * 16 代替 a << 4。
這個技巧也能在一定程度上提高性能,但是如果你不擅長,那就不要強求,因為當代計算機的性能已經(jīng)非常強大了,沒必要為了一個程序而犧牲代碼的可讀性。
嘗試使用單例模式
如果我們設計一個不需要考慮線程安全的類,請用單例模式來使用這個類,這樣可以節(jié)省內(nèi)存。幸運的是,對于我們使用的spring框架,Java bean默認是單例的。
降低鎖粒度
假設我們有一個共享文檔編輯功能,用戶會同時編輯共享文檔。為了保證文件的正確性,我們需要使用線程安全synchronized來保證。很多初學者可能會這樣寫。
如果采用上述方式,只有一個線程可以進入同步代碼塊執(zhí)行,其他線程只能掛起等待,即使這些線程可能寫入不同的文件。我們可以通過降低鎖粒度來提高性能。
不要隨意使用靜態(tài)變量
如果你熟悉JVM基礎知識,那么就會知道如果一個對象被定義為靜態(tài)變量,這個變量的引用就不容易被垃圾回收器回收。
靜態(tài)變量“a”的生命周期與測試類相同。只要測試類型沒有被卸載,“a”的引用對象就會駐留在內(nèi)存中,直到程序終止。
使用基本數(shù)據(jù)類型
在應用程序中使用基本數(shù)據(jù)類型來減少內(nèi)存消耗并提高程序性能。如果可以使用 int,請不要使用其 Integer 包裝類型,使用double 而不是 Double。
基本數(shù)據(jù)類型的包裝類實例存放在堆內(nèi)存中,每次使用都會在堆內(nèi)存中創(chuàng)建一個。如果使用基本數(shù)據(jù)類型,數(shù)據(jù)存放在棧幀中,棧的訪問速度可比堆快很多。






























