1. 程式人生 > >11 個簡練的 Java 性能調優技巧

11 個簡練的 Java 性能調優技巧

Java

想要讓你的項目一直高性能運作嗎?以下有一些技巧你可以拿去消除緩存瓶頸,還有一些其他的性能調優建議。

大多數開發者認為性能優化是一個復雜的話題,它需要大量的工作經驗和相關知識理論。好吧,這也不完全錯。優化一個應用做到性能最優化可能不是件容易的任務,但是這並不意味著你沒有相關的知識就什麽也做不了。這裏有一些易於遵循的建議和最佳實踐可以幫助你創建一個性能良好的應用程序。

這些建議的大部分都是針對 Java 語言的。但是也有一些是跟語言無關的,你可以運用到任意的應用和程序中。在我們學習特定的 Java 編程性能調優之前,先來探討一些通用的技巧。

  1. 在明確必要之前別急著優化

這可能是最重要的性能優化技巧之一。你應該遵循常見的最佳實踐做法並在案例中高效地應用它。但是這並不意味在證明必要之前,你應該更換任何標準庫或構建復雜的優化。

多數情況下,過早地優化會占用大量的時間,而且會使代碼變得難以理解和閱讀。更糟糕的是,這些優化通常並沒帶來任何好處,因為你花了大量的時間在優化應用中的非關鍵部分。

那麽,要怎麽證明東西需要優化呢?

首先,你需要定義你的代碼速度得多快。例如,為所有 API 調用指定最大響應時間,或者指定在特定時間範圍內要導入的記錄數量。在做完這些後,你需要確定你應用中哪些部分太慢需要改進。當完成這些後,你就可以來看看第二個技巧提示。

  1. 使用分析器找到真正的瓶頸

在完成第一部分的優化建議以鑒別出你應用中需要提升的部分後,要從哪裏入手呢?

你可以有兩種途徑來解決這個問題:

1)查看你的代碼,從看起來可疑的或者你覺得可能會導致出現問題的地方入手

2)或者使用分析器獲取代碼每個部分的行為(執行過程)和性能的詳細信息。

希望我不需要解釋為什麽應該始終遵循第二種途徑/方法的原因。

很顯然,基於分析器的方式可以讓你更好地理解代碼的性能影響,並允許你去專註於更關鍵的部分(代碼)。即使你曾經使用過分析器,你一定記得你曾經多麽驚訝於一下就找到了代碼的哪些部分產生了性能問題。我第一次的猜測不止一次地導致我走錯了方向。

  1. 為整個應用程序創建一個性能測試套件

這是另一個通用的可以幫助你避免在將性能改進部署到產品中之後經常會發生的許多意外問題的技巧。你應該總是定義一個性能測試套件來測試整個應用程序,並在性能改進之前和之後運行它。

這些額外的測試運行將幫助你識別你的改動所引起的功能和性能上的副作用,並確保不會導致弊大於利的更新。如果你處理的是被應用程序的多個不同部分使用的組件,如數據庫或緩存,那這一點尤為重要。

  1. 優先關註最大瓶頸

在創建了測試套件並使用分析器分析你的應用程序之後,你可以列出一系列需要解決以提高性能的問題列表。這很好,但這並沒有回答你需要從哪裏開始的問題。你可以專註於速成方案,或從最重要的問題開始。

速成方案一開始可能會很有吸引力,因為你可以很快顯示第一個成果。但有時,可能有必要說服其他團隊成員或管理層認為性能分析是值得的。

一般來說,我建議從頂層開始,首先開始處理最重要的性能問題。這將為你提供最大的性能改進,而且你可能僅需要解決這些問題中的一小部分就能滿足你的性能要求。

常見的通用調優技巧到此結束。接下來讓我們仔細看看一些特定於 Java 的技巧。

  1. 使用 StringBuilder 以編程方式連接字符串

在 Java 中有很多不同的選項來連接字符串。例如,你可以使用簡單的 + 或 + = ,以及老的 StringBuffer 或 StringBuilder 。

那麽,你應該選擇哪種方法呢?

答案取決於連接字符串的代碼。如果你是以編程方式將新內容添加到字符串中,例如在 for 循環中,則應使用 StringBuilder 。它很易於使用,並提供比 StringBuffer 更好的性能。但請記住,與 StringBuffer 相比, StringBuilder 不是線程安全的,可能並不適用於所有情況。

你只需要實例化一個新的 StringBuilder 並調用 append 方法來向 String 中添加一個新的部分。在你添加完了所有的部分後,你可以調用 toString() 方法來檢索已連接的字符串。

下面的代碼片段展示了一個簡單的例子。在每次叠代期間,該循環將 i 轉換為一個 String ,並將其與空格一起添加到StringBuilder sb 中。所以,最後,這段代碼在日誌文件中寫入 “This is a test0 1 2 3 4 5 6 7 8 9” 。

StringBuilder sb =newStringBuilder(“This is a test”);for(inti=0; i<10; i++) {

sb.append(i);

sb.append(” “);

}

log.info(sb.toString());

正如你在我們的公眾號Java技術棧代碼片段中看到的,我們可以為字符串的第一個元素提供到構造函數中。這會創建一個StringBuilder ,其中包含了你所提供的字符串以及 16 個額外字符的容量。當你向StringBuilder 中添加更多字符時,你的 JVM 將動態的增加StringBuilder 的大小。

如果你已經知道字符串將包含多少個字符,則可以將該數字提供給不同的構造方法以實例化具有指定容量的 StringBuilder 。這進一步提高了效率,因為它不需要動態擴展其容量。

  1. 使用 + 連接一個語句中的字符串

當你使用 Java 實現你的第一個應用程序時,可能有人告訴過你不要使用 + 來連接字符串。如果你是在應用程序邏輯內連接字符串的話,這是對的。字符串是不可變的,每個字符串的連接結果都被存儲在一個新的字符串對象中。這需要額外的存儲空間,並可能使你的應用程序運行緩慢,特別是當你在一個循環內連接多個字符串的情況下。

在這些情況下,你應該遵循技巧 5 中的內容,並使用StringBuilder 。

但如果你只是將字符串分成多行來改善代碼的可讀性,這並不適用。

Query q = em.createQuery(“SELECTa.id, a.firstName, a.lastName ”

  • “FROMAuthor a ”

  • “WHEREa.id = :id”);

在這些情景下,你應該使用簡單的 + 來連接字符串。你的 Java 編譯器會優化它,並在編譯時完成連接。因此,在運行時,你的代碼將只使用一個字符串,並不需要任何連接操作。

  1. 盡可能使用基本類型

避免任何開銷並提高應用程序性能的另一種簡便快速的方法是使用基本類型而不是其包裝類。所以,最好使用 int 而不是 Integer ,是 double 而不是 Double 。這將使得你的 JVM 將值存儲在堆棧而不是堆中,以減少內存消耗,並更有效地處理它。

  1. 盡量避免大整數和小數

由於我們已經在討論數據類型,所以我們也應該快速瀏覽大整數和小數。尤其是後者因其精確性而受歡迎。但這是有代價的。

大整數和小數比一個簡單的long型或double型需要更多的內存,並會顯著減慢所有的運算。所以,如果你需要額外的精度,或者如果你的數字超出一個較長的範圍,最好要三思。這可能是你需要更改並解決性能問題的唯一方法,尤其是在實現數學算法時。

  1. 優先檢查當前日誌級別

這個建議應該是顯而易見的,但不幸的是,很多人在寫代碼的時候都會忽略它。 在創建調試消息之前,應該總是優先檢查當前日誌級別。 否則,你可能會創建一個附加你日誌消息的字符串,而該字符串之後將被忽略。

這裏有兩個你不應該這樣做的反面例子。

// don’t do this

log.debug(“User[” + userName + “]called method X with[” + i + “]”);

// or this

log.debug(String.format(“User[%s]called method X with[%d]”, userName,i));

在這兩個示例中,你都將執行創建日誌消息所有必需的步驟,而不知道日誌框架是否將使用日誌消息。 因此在創建調試消息之前,最好先檢查當前的日誌級別。

// do thisif (log.isDebugEnabled()) {

log.debug(“User[” + userName + “]called method X with[” + i + “]”);

}

  1. 使用 Apache Commons StringUtils.Replace 而不是 String.replace

一般來說,String.replace 方法可以正常工作,並且效率很高,尤其是在你使用 Java 9 的情況下。但是,如果你的應用程序需要大量的替換操作,並且沒有更新到最新的 Java 版本,那麽檢查更快和更有效的替代品依然是有必要的。

有一種候選方案是 Apache Commons Lang 的StringUtils.replace 方法。正如 Lukas Eder 在他最近的一篇博客文章中所描述的,它遠遠勝過了 Java 8 的 String.replace 方法。

而且它只需要很小的改動。你只需要將 Apache Commons Lang 項目的 Maven 依賴項添加到你的應用程序的 pom.xml 中,並將 String.replacemethod 的所有調用替換為 StringUtils.replace 方法。

// replace this

test.replace(“test”, “simpletest”);

// with this

StringUtils.replace(test, “test”, “simpletest”);

11.昂貴的緩存資源,如數據庫連接

緩存是避免重復執行昂貴或常用代碼片段的流行解決方案。總的思路很簡單:重復使用這些資源比創建一個新的資源更劃算。

一個典型的例子是緩存池中的數據庫連接。新連接的創建需要時間,如果你重用現有連接,則可以避免這種情況。

你也可以在 Java 語言源碼中找到其他的例子。例如,在 Integer 類中的valueOf方法緩存了介於 -128 到 127 之間的值。你可能會說創建一個新的 Integer 並不是太昂貴,但是由於它經常被使用,因此緩存最常用的值也可以×××能優勢。

但是,當你考慮使用緩存時,請記住緩存實現也會產生開銷。你需要花費額外的內存來儲存可重復使用的資源,因此你可能需要管理你的緩存以使資源可訪問,並刪除過期的資源。

所以,在開始緩存任何資源之前,請確保它們是經常使用的,以超過緩存實現的開銷(代價)。

總結

正如你所看到的,有時不需要太多的工作就可以提高你的應用程序的性能。本文中的大部分建議只需要稍作努力就可以將它們應用於你的代碼中。

但還是那句話,最重要的還是那些與是什麽編程語言無關的技巧:

在你知道其必要性之前不要進行優化

使用分析器(profiler)來查找真正的瓶頸

優先處理最大的瓶頸

1、具有1-5工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加群。

2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加群。

3、如果沒有工作經驗,但基礎非常紮實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加群。

4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加群。

5.群號 744677563

6.阿裏Java高級大牛直播講解知識點,分享知識,知識點都是各位老師多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知!

11 個簡練的 Java 性能調優技巧