Java性能調優(yōu)準則

大家在寫代碼的時候是不是都只考慮了實現(xiàn),沒有考慮性能呢?如果說你只是做業(yè)務系統(tǒng)的增刪改查并且業(yè)務量不大的話。這是毋庸置疑的栏账。但是如果你在較大吞吐量和較小的資源的時候梁钾。你的程序想保持正常運行嗎? 以下是您可以采取的一些步驟來消除瓶頸伦意,緩存的技巧以及其他性能調整建議链沼。

image

請點擊此處輸入圖片描述

大多數(shù)開發(fā)人員感覺性能優(yōu)化是一個非常復雜的話題默赂,并且是需要大量的經(jīng)驗和知識的。 當然這說的是有道理的括勺。 優(yōu)化應用程序以獲得最佳性能不是一件容易的事情缆八。 但是疾捍,這并不意味著如果你沒有獲得這些知識,就不能做任何事情奖恰。 有幾個易于遵循的建議和最佳實踐可以幫助您創(chuàng)建一個性能良好的應用程序。

這些建議中的大部分都是針對Java的论泛。 但也有幾個與語言無關的語言,您可以將其應用于所有應用程序和編程語言屁奏。 在討論特定于Java的性能調優(yōu)技巧之前错负,先談談其中的一些通用準則。

不要在沒有必要的時候做性能調優(yōu)

這可能是最重要的性能調優(yōu)的準則之一折联。只要你根據(jù)最佳實踐或者推薦的方法實現(xiàn)了你的程序就行了识颊。沒有必要在任何時候開始討論如何優(yōu)化到最佳的性能。

在大多數(shù)情況下怕享,過早進行性能優(yōu)化會占用大量時間镰踏,并使代碼難以閱讀和維護。更糟糕的是跌帐,這些優(yōu)化通常不會帶來任何好處绊率,因為您花費大量時間來優(yōu)化應用程序的非關鍵部分。

那么脸狸,你如何來界定你需要做性能優(yōu)化了呢藐俺?

首先,您需要判斷應用程序代碼的速度是否如預期卿啡。例如菱父,為所有API調用設定一個最大響應時間剑逃,或者在特定時間范圍內(nèi)要導入的記錄數(shù)官辽。完成之后,您可以測量應用程序的哪些部分太慢称开,需要改進乓梨。當你這樣做的時候清酥,你應該看看下下一個準則焰轻。

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

在遵循第一個準則并確定了應用程序需要進行性能調優(yōu)的部分后要怎么開始下手呢?

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

  1. 你可以看看你的代碼辱志,并開始看起來可疑的部分揩懒,或者你覺得可能會產(chǎn)生問題的部分。

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

我希望我不需要解釋為什么你應該始終遵循第二種方法智亮。

很明顯,基于分析器的方法可以讓您更好地理解代碼的性能影響阔蛉,并使您能夠專注于最關鍵的部分状原。 如果您曾經(jīng)使用過一個分析器,那么您將會記得一些情況颠区,在這些情況下瓦呼,您對代碼的哪些部分產(chǎn)生了性能問題感到驚訝测暗。 我不止一次的第一次猜測會導致我走錯了方向磨澡。

為整個應用程序創(chuàng)建一個性能測試SuitCase

這是另一個通用規(guī)則,可以幫助您避免將性能改進部署到生產(chǎn)后經(jīng)常發(fā)生的許多意外問題稚字。 您應該總是定義一個性能測試套件來測試整個應用程序厦酬,并在性能改進之前和之后運行它。

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

先進行最大的瓶頸上工作

在創(chuàng)建測試套件并使用分析器分析您的應用程序之后,您會列出一系列需要解決的問題以提高性能瑟曲。 這很好豪治,但它仍然不能回答你應該從哪里開始负拟。 您可以專注于快速獲勝,或從最重要的問題開始掩浙。

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

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

足夠的一般性能調整技巧。 讓我們仔細看看一些特定于Java的性能調優(yōu)細節(jié)请敦。

使用StringBuilder來連接字符串

有很多不同的選項來連接Java中的字符串侍筛。例如撒穷,您可以使用簡單的+或+ =,StringBuffer或一個StringBuilder禽笑。

那么蛤奥,你應該選擇哪種方法?

答案取決于連接字符串的代碼。如果以編程方式將新內(nèi)容添加到字符串中缅刽,例如在for循環(huán)中迟蜜,則應使用StringBuilder啡省。它很容易使用髓霞,并提供比StringBuffer更好的性能酸茴。但請記住兢交,與StringBuffer相比,StringBuilder不是線程安全的酪穿,可能不適合所有用例晴裹。

你只需要實例化一個新的StringBuilder并調用append方法來向String中添加一個新的部分涧团。而當你添加了所有的部分,你可以調用toString()方法來檢索連接的字符串钮追。

下面的代碼片段顯示了一個簡單的例子阿迈。在每次迭代期間,這個循環(huán)將i轉換為一個String刊棕,并將它與一個空格一起添加到StringBuilder sb中待逞。所以,最后蜈膨,這段代碼在日志文件中寫入“This is a test0 1 2 3 4 5 6 7 8 9”牺荠。

image

請點擊此處輸入圖片描述

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

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

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

當你用Java實現(xiàn)你的第一個應用程序時鹤盒,可能有人告訴過你不應該用+來連接字符串。 如果您在應用程序邏輯中連接字符串驼鞭,這是正確的率触。 字符串是不可變的汇竭,每個字符串連接的結果都存儲在一個新的String對象中细燎。 這需要額外的內(nèi)存,并減慢你的應用程序玻驻,特別是如果你在一個循環(huán)內(nèi)連接多個字符串璧瞬。

在這些情況下,您應該遵循上面的規(guī)則并使用StringBuilder渔欢。

但是瘟忱,如果您只是將字符串分成多行來改善代碼的可讀性苫幢,情況并非如此韩肝。

image

請點擊此處輸入圖片描述

在這些情況下九榔,你應該用一個簡單的+來連接你的字符串帚屉。 您的Java編譯器將優(yōu)化這個并在編譯時執(zhí)行連接。 所以喻旷,在運行時牢屋,你的代碼將只使用1個字符串,不需要連接锋谐。

盡可能使用基本數(shù)據(jù)

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

盡量避免使用BigInteger和BigDecimal

由于我們已經(jīng)在討論數(shù)據(jù)類型爷恳,所以我們也應該快速瀏覽一下BigInteger和BigDecimal。 尤其是后者因其精確性而受歡迎妒貌。 但是這是有代價的。

BigInteger和BigDecimal需要更多的內(nèi)存比一個long或double菊碟,并且看起來會降低所有的運行效率在刺。 所以蚣驼,如果你需要額外的精度,或者如果你的數(shù)字將超過一個長的范圍纯陨,最好三思留储。 這可能是您需要更改以解決性能問題的唯一方法,特別是在實施數(shù)學算法時阴颖。

檢查當前日志級別

這個建議應該是顯而易見的丐膝,但不幸的是帅矗,你可以找到很多忽略它的代碼。 在創(chuàng)建調試消息之前软棺,應該始終首先檢查當前日志級別尤勋。 否則最冰,您可能會創(chuàng)建一個字符串與您的日志消息稀火,將被忽略之后。

這里有兩個例子篇裁,不建議你這樣做。

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

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

在這兩種情況下团甲,您都將執(zhí)行所有必需的步驟來創(chuàng)建日志消息躺苦,而不知道日志框架是否將使用日志消息产还。 在創(chuàng)建調試消息之前,最好先檢查當前的日志級別愈诚。正確的寫法應該是這樣的:

if (log.isDebugEnabled()) {

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

}

使用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.replace方法的所有調用替換為StringUtils.replace方法铣墨。

緩存開銷量較大的資源办绝,如數(shù)據(jù)庫連接等

緩存是避免重復執(zhí)行昂貴或經(jīng)常使用的代碼片段的常用解決方案孕蝉。總的想法很簡單:重復使用這些資源比反復創(chuàng)建新資源要便宜超埋。

一個典型的例子是緩存池中的數(shù)據(jù)庫連接佳鳖。新連接的創(chuàng)建需要時間,如果您重新使用現(xiàn)有連接来庭,則可以避免這種情況月弛。

您還可以在Java語言本身中找到其他示例科盛。 Integer類的valueOf方法一樣,例如厉萝,緩存你可能會說榨崩,一個新的整數(shù)的創(chuàng)作是不是太昂貴-128到127之間的值蜡饵,但它的使用經(jīng)常是最常用的值的高速緩存提供性能優(yōu)勢胳施。

但是,當您考慮緩存時博杖,請記住您的緩存實現(xiàn)也會產(chǎn)生開銷筷登。您需要花費額外的內(nèi)存來存儲可重用資源,您可能需要管理緩存以使資源可訪問或刪除過時的資源狈醉。

因此苗傅,在開始緩存任何資源之前班巩,請確保您經(jīng)常使用它們來超過緩存實施的開銷。

image

請點擊此處輸入圖片描述

總結

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

但其實户秤,最重要的建議是語言無關的:

  • 不要在你知道這是必要的之前進行優(yōu)化

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

  • 首先處理最大的瓶頸

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市转砖,隨后出現(xiàn)的幾起案子鲸伴,更是在濱河造成了極大的恐慌汞窗,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝌焚,死亡現(xiàn)場離奇詭異誓斥,居然都是意外死亡劳坑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門涝开,熙熙樓的掌柜王于貴愁眉苦臉地迎上來框仔,“玉大人忠寻,你說我怎么就攤上這事〈婧停” “怎么了奕剃?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捐腿。 經(jīng)常有香客問我纵朋,道長,這世上最難降的妖魔是什么茄袖? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任操软,我火速辦了婚禮,結果婚禮上宪祥,老公的妹妹穿的比我還像新娘。我一直安慰自己蝗羊,他們只是感情好藏澳,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耀找,像睡著了一般翔悠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上野芒,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天蓄愁,我揣著相機與錄音,去河邊找鬼狞悲。 笑死撮抓,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的摇锋。 我是一名探鬼主播丹拯,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼站超,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咽笼?” 一聲冷哼從身側響起顷编,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤戚炫,失蹤者是張志新(化名)和其女友劉穎剑刑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體双肤,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡施掏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茅糜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片七芭。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蔑赘,靈堂內(nèi)的尸體忽然破棺而出狸驳,到底是詐尸還是另有隱情,我是刑警寧澤缩赛,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布耙箍,位于F島的核電站,受9級特大地震影響酥馍,放射性物質發(fā)生泄漏辩昆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一旨袒、第九天 我趴在偏房一處隱蔽的房頂上張望汁针。 院中可真熱鬧,春花似錦砚尽、人聲如沸施无。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帆精。三九已至,卻和暖如春隧魄,著一層夾襖步出監(jiān)牢的瞬間卓练,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工购啄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留襟企,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓狮含,卻偏偏與公主長得像顽悼,于是被迫代替她去往敵國和親曼振。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蔚龙,服務發(fā)現(xiàn)冰评,斷路器,智...
    卡卡羅2017閱讀 134,667評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法木羹,類相關的語法甲雅,內(nèi)部類的語法,繼承相關的語法坑填,異常的語法抛人,線程的語...
    子非魚_t_閱讀 31,643評論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,182評論 25 707
  • 風雨兼程,一把傘脐瑰,有你有我妖枚; 攜手未來,一條路苍在,不離不棄绝页; 未曾得到,何患失去寂恬。 我用橄欖編織出一場美夢续誉, 夢的盡...
    廉吉閱讀 361評論 0 0
  • 我總是想寫文章,想寫復雜的掠剑,內(nèi)涵的屈芜,完整地概括我的想法經(jīng)歷。我的經(jīng)歷在同齡人中朴译,算得上是豐富井佑,一波三折折而又折,雖...
    楓影頡閱讀 306評論 0 3