05.24 性能優化|Java性能調優準則-攻略

大家在寫代碼的時候是不是都只考慮了實現,沒有考慮性能呢?如果說你只是做業務系統的增刪改查並且業務量不大的話。這是毋庸置疑的。但是如果你在較大吞吐量和較小的資源的時候。你的程序想保持正常運行嗎?

以下是您可以採取的一些步驟來消除瓶頸,緩存的技巧以及其他性能調整建議。

大多數開發人員感覺性能優化是一個非常複雜的話題,並且是需要大量的經驗和知識的。 當然這說的是有道理的。 優化應用程序以獲得最佳性能不是一件容易的事情。 但是,這並不意味著如果你沒有獲得這些知識,就不能做任何事情。 有幾個易於遵循的建議和最佳實踐可以幫助您創建一個性能良好的應用程序。

在討論特定於Java的性能調優技巧之前,先談談其中的一些通用準則。

1

不要在沒有必要的時候做性能調優化

這可能是最重要的性能調優的準則之一。只要你根據最佳實踐或者推薦的方法實現了你的程序就行了。沒有必要在任何時候開始討論如何優化到最佳的性能。

在大多數情況下,過早進行性能優化會佔用大量時間,並使代碼難以閱讀和維護。更糟糕的是,這些優化通常不會帶來任何好處,因為您花費大量時間來優化應用程序的非關鍵部分。

那麼,你如何來界定你需要做性能優化了呢?

首先,您需要判斷應用程序代碼的速度是否如預期。例如,為所有API調用設定一個最大響應時間,或者在特定時間範圍內要導入的記錄數。完成之後,您可以測量應用程序的哪些部分太慢,需要改進。當你這樣做的時候,你應該看看下面這個準則。

2

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

在遵循第一個準則並確定了應用程序需要進行性能調優的部分後要怎麼開始下手呢?

有兩個辦法來開始我們的第一刀:

  1. 你可以看看你的代碼,並開始看起來可疑的部分,或者你覺得可能會產生問題的部分。

  2. 或者您使用一個分析器並獲取有關您的代碼的每個部分的行為和性能的詳細信息。

很明顯,基於分析器的方法可以讓您更好地理解代碼的性能影響,並使您能夠專注於最關鍵的部分。 如果您曾經使用過一個分析器,那麼您將會記得一些情況,在這些情況下,您對代碼的哪些部分產生了性能問題感到驚訝。 我不止一次的第一次猜測會導致我走錯了方向。

3

為整個應用創建一個性能測試

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

這些額外的測試運行將幫助您確定更改的功能和性能副作用,並確保不會導致造成更多損害的更新。 如果您處理由應用程序的多個不同部分使用的組件,如數據庫或緩存,這一點尤其重要。

4

先進行最大的瓶頸上工作

在創建測試套件並使用分析器分析您的應用程序之後,您會列出一系列需要解決的問題以提高性能。 這很好,但它仍然不能回答你應該從哪裡開始。 您可以專注於快速獲勝,或從最重要的問題開始。

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

但總的來說,我建議從頂層開始,首先開始處理最重要的性能問題。 這將為您提供最大的性能改進,而且您可能不需要解決這些問題中的一些以滿足您的性能要求。

5

使用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”。

<table><tbody>

StringBuilder sb = new StringBuilder("This is a test");

for (int i = 0; i < 10; i++) {

sb.append(i);

sb.append(" ");

}

log.info(sb.toString());

/<tbody>/<table>

正如你可以在代碼片段中看到的那樣,你可以將String的第一個元素提供給構造方法。 這將創建一個新的StringBuilder包含提供的字符串和16個額外的字符的容量。 當您向StringBuilder添加更多字符時,您的JVM將動態增加StringBuilder的大小。

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

6

在一個語句中使用+連接字符串

當你用Java實現你的第一個應用程序時,可能有人告訴過你不應該用+來連接字符串。 如果您在應用程序邏輯中連接字符串,這是正確的。 字符串是不可變的,每個字符串連接的結果都存儲在一個新的String對象中。 這需要額外的內存,並減慢你的應用程序,特別是如果你在一個循環內連接多個字符串。

在這些情況下,您應該遵循上面的規則並使用StringBuilder。

但是,如果您只是將字符串分成多行來改善代碼的可讀性,情況並非如此。

<table><tbody>

Query q = em.createQuery("select a.id,a.firstName,a.lastName" + " from tablename a " +"where a.id = :id");

/<tbody>/<table>

在這些情況下,你應該用一個簡單的+來連接你的字符串。 您的Java編譯器將優化這個並在編譯時執行連接。 所以,在運行時,你的代碼將只使用1個字符串,不需要連接。

7

儘可能使用基本數據

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

8

儘量避免使用BigInteger和BigDecimal

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

BigInteger和BigDecimal需要更多的內存比一個long或double,並且看起來會降低所有的運行效率。 所以,如果你需要額外的精度,或者如果你的數字將超過一個長的範圍,最好三思。 這可能是您需要更改以解決性能問題的唯一方法,特別是在實施數學算法時。

9

檢查當前日誌級別

這個建議應該是顯而易見的,但不幸的是,你可以找到很多忽略它的代碼。 在創建調試消息之前,應該始終首先檢查當前日誌級別。 否則,您可能會創建一個字符串與您的日誌消息,將被忽略之後。

這裡有兩個例子,不建議你這樣做。

<table><tbody>

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

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

/<tbody>/<table>

在這兩種情況下,您都將執行所有必需的步驟來創建日誌消息,而不知道日誌框架是否將使用日誌消息。 在創建調試消息之前,最好先檢查當前的日誌級別。正確的寫法應該是這樣的:

<table><tbody>

if(log.isDebugEnable()){

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

}

/<tbody>/<table>

10

用StringUtils來代替String之間的API

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

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

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

11

緩存開銷量比較大的

緩存是避免重複執行昂貴或經常使用的代碼片段的常用解決方案。總的想法很簡單:重複使用這些資源比反覆創建新資源要便宜。

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

您還可以在Java語言本身中找到其他示例。 Integer類的valueOf方法一樣,例如,緩存你可能會說,一個新的整數的創作是不是太昂貴-128到127之間的值,但它的使用經常是最常用的值的高速緩存提供性能優勢。

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

因此,在開始緩存任何資源之前,請確保您經常使用它們來超過緩存實施的開銷。


歡迎踴躍留言,模模會第一時間回覆哦!


分享到:


相關文章: