10個(gè)簡單的 Java 性能調(diào)優(yōu)技巧

? 優(yōu)化應(yīng)用程序以獲得最佳性能不是一件容易的事情堰塌。但是,這并不意味著如果你不具備這些知識荷辕,就不能做任何事情昧甘。這里有11個(gè)易于遵循的建議和最佳實(shí)踐可以幫助你創(chuàng)建一個(gè)性能良好的應(yīng)用程序命黔。

大部分建議是針對Java的呜呐。但也有若干建議是與語言無關(guān)的,可以應(yīng)用于所有應(yīng)用程序和編程語言悍募。在討論專門針對Java的性能調(diào)優(yōu)技巧之前蘑辑,讓我們先來看看通用技巧。

1.在你知道必要之前不要優(yōu)化

這可能是最重要的性能調(diào)整技巧之一坠宴。你應(yīng)該遵循常見的最佳實(shí)踐做法并嘗試高效地實(shí)現(xiàn)用例洋魂。但是,這并不意味著在你證明必要之前,你應(yīng)該更換任何標(biāo)準(zhǔn)庫或構(gòu)建復(fù)雜的優(yōu)化副砍。

在大多數(shù)情況下衔肢,過早優(yōu)化不但會占用大量時(shí)間,而且會使代碼變得難以閱讀和維護(hù)豁翎。更糟糕的是角骤,這些優(yōu)化通常不會帶來任何好處,因?yàn)槟慊ㄙM(fèi)大量時(shí)間來優(yōu)化的是應(yīng)用程序的非關(guān)鍵部分心剥。

那么启搂,你如何證明你需要優(yōu)化一些東西呢?

首先刘陶,你需要定義應(yīng)用程序代碼的速度得多快,例如牢撼,為所有API調(diào)用指定最大響應(yīng)時(shí)間匙隔,或者指定在特定時(shí)間范圍內(nèi)要導(dǎo)入的記錄數(shù)量。在完成這些之后熏版,你就可以測量應(yīng)用程序的哪些部分太慢需要改進(jìn)纷责。然后,接著看第二個(gè)技巧撼短。

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

在你遵循第一個(gè)建議并確定了應(yīng)用程序的某些部分需要改進(jìn)后再膳,那么從哪里開始呢?

你可以用兩種方法來解決問題:

查看你的代碼曲横,并從看起來可疑或者你覺得可能會產(chǎn)生問題的部分開始喂柒。

或者使用分析器并獲取有關(guān)代碼每個(gè)部分的行為和性能的詳細(xì)信息。

希望不需要我解釋為什么應(yīng)該始終遵循第二種方法的原因禾嫉。

很明顯灾杰,基于分析器的方法可以讓你更好地理解代碼的性能影響,并使你能夠?qū)W⒂谧铌P(guān)鍵的部分熙参。如果你曾使用過分析器艳吠,那么你一定記得曾經(jīng)你是多么驚訝于一下就找到了代碼的哪些部分產(chǎn)生了性能問題。老實(shí)說孽椰,我第一次的猜測不止一次地導(dǎo)致我走錯(cuò)了方向昭娩。

3.為整個(gè)應(yīng)用程序創(chuàng)建性能測試套件

這是另一個(gè)通用技巧,可以幫助你避免在將性能改進(jìn)部署到生產(chǎn)后經(jīng)常會發(fā)生的許多意外問題黍匾。你應(yīng)該總是定義一個(gè)測試整個(gè)應(yīng)用程序的性能測試套件栏渺,并在性能改進(jìn)之前和之后運(yùn)行它。

這些額外的測試運(yùn)行將幫助你識別更改的功能和性能副作用膀捷,并確保不會導(dǎo)致弊大于利的更新迈嘹。如果你工作于被應(yīng)用程序若干不同部分使用的組件,如數(shù)據(jù)庫或緩存,那么這一點(diǎn)就尤其重要秀仲。

4.首先處理最大的瓶頸

在創(chuàng)建測試套件并使用分析器分析應(yīng)用程序之后融痛,你可以列出一系列需要解決以提高性能的問題。這很好神僵,但它仍然不能回答你應(yīng)該從哪里開始的問題雁刷。你可以專注于速效方案,或從最重要的問題開始保礼。

速效方案一開始可能會很有吸引力沛励,因?yàn)槟憧梢院芸祜@示第一個(gè)成果。但有時(shí)炮障,可能需要你說服其他團(tuán)隊(duì)成員或管理層認(rèn)為性能分析是值得的——因?yàn)闀簳r(shí)看不到效果目派。

但總的來說,我建議首先處理最重要的性能問題胁赢。這將為你提供最大的性能改進(jìn)企蹭,而且可能再也不需要去解決其中一些為了滿足性能需求的問題。

常見的性能調(diào)整技巧到此結(jié)束智末。下面讓我們仔細(xì)看看一些特定于Java的技巧谅摄。

5.使用StringBuilder以編程方式連接String

有很多不同的選項(xiàng)來連接Java中的String。例如系馆,你可以使用簡單的+或+ =送漠,以及StringBuffer或StringBuilder。

那么由蘑,你應(yīng)該選擇哪種方法闽寡?

答案取決于連接String的代碼。如果你是以編程方式添加新內(nèi)容到String中尼酿,例如在for循環(huán)中下隧,那么你應(yīng)該使用StringBuilder。它很容易使用谓媒,并提供比StringBuffer更好的性能淆院。但請記住,與StringBuffer相比句惯,StringBuilder不是線程安全的土辩,可能不適合所有用例。

你只需要實(shí)例化一個(gè)新的StringBuilder并調(diào)用append方法來向String中添加一個(gè)新的部分抢野。在你添加了所有的部分之后拷淘,你就可以調(diào)用toString()方法來檢索連接的String。

下面的代碼片段顯示了一個(gè)簡單的例子指孤。在每次迭代期間启涯,這個(gè)循環(huán)將i轉(zhuǎn)換為一個(gè)String贬堵,并將它與一個(gè)空格一起添加到StringBuilder sb中。所以结洼,最后黎做,這段代碼將在日志文件中寫入“This is a test0 1 2 3 4 5 6 7 8 9”。

StringBuilder sb = new StringBuilder(“This is a test”);

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

sb.append(i);

sb.append(” “);

}

log.info(sb.toString());

正如在代碼片段中看到的那樣松忍,你可以將String的第一個(gè)元素提供給構(gòu)造方法蒸殿。這將創(chuàng)建一個(gè)新的StringBuilder,新的StringBuilder包含提供的String和16個(gè)額外字符的容量鸣峭。當(dāng)你向StringBuilder添加更多字符時(shí)宏所,JVM將動態(tài)增加StringBuilder的大小。

如果你已經(jīng)知道你的String將包含多少個(gè)字符摊溶,則可以將該數(shù)字提供給不同的構(gòu)造方法以實(shí)例化具有定義容量的StringBuilder爬骤。這進(jìn)一步提高了效率,因?yàn)樗恍枰獎討B(tài)擴(kuò)展其容量莫换。

6.使用+連接一個(gè)語句中的String

當(dāng)你用Java實(shí)現(xiàn)你的第一個(gè)應(yīng)用程序時(shí)盖腕,可能有人告訴過你不應(yīng)該用+來連接String。如果你是在應(yīng)用程序邏輯中連接字符串浓镜,這是正確的。字符串是不可變的劲厌,每個(gè)字符串的連接結(jié)果都存儲在一個(gè)新的String對象中膛薛。這需要額外的內(nèi)存,會減慢你的應(yīng)用程序补鼻,特別是如果你在一個(gè)循環(huán)內(nèi)連接多個(gè)字符串的話哄啄。

在這些情況下,你應(yīng)該遵循技巧5并使用StringBuilder风范。

但是咨跌,如果你只是將字符串分成多行來改善代碼的可讀性,那情況就不一樣了硼婿。

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

+ “FROM Author a ”

+ “WHERE a.id = :id”);

在這些情況下锌半,你應(yīng)該用一個(gè)簡單的+來連接你的字符串。Java編譯器會對此優(yōu)化并在編譯時(shí)執(zhí)行連接寇漫。所以刊殉,在運(yùn)行時(shí),你的代碼將只使用1個(gè)String州胳,不需要連接记焊。

7.盡可能使用基元

避免任何開銷并提高應(yīng)用程序性能的另一個(gè)簡便而快速的方法是使用基本類型而不是其包裝類。所以栓撞,最好使用int來代替Integer遍膜,使用double來代替Double碗硬。這允許JVM將值存儲在堆棧而不是堆中以減少內(nèi)存消耗,并作出更有效的處理瓢颅。

8.試著避免BigInteger和BigDecimal

既然我們在討論數(shù)據(jù)類型恩尾,那么我們也快速瀏覽一下BigIntegerBigDecimal吧。尤其是后者因其精確性而受到大家的歡迎惜索。但是這是有代價(jià)的特笋。

BigInteger和BigDecimal比簡單的long或double需要更多的內(nèi)存,并且會顯著減慢所有計(jì)算巾兆。所以猎物,你如果需要額外的精度,或者數(shù)字將超過long的范圍角塑,那么最好三思而后行蔫磨。這可能是你需要更改以解決性能問題的唯一方法,特別是在實(shí)現(xiàn)數(shù)學(xué)算法的時(shí)候圃伶。

9.首先檢查當(dāng)前日志級別

這個(gè)建議應(yīng)該是顯而易見的堤如,但不幸的是,很多程序員在寫代碼的時(shí)候都會大多會忽略它窒朋。在你創(chuàng)建調(diào)試消息之前搀罢,始終應(yīng)該首先檢查當(dāng)前日志級別。否則侥猩,你可能會創(chuàng)建一個(gè)之后會被忽略的日志消息字符串榔至。

這里有兩個(gè)反面例子。

// 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));

在上面兩種情況中欺劳,你都將執(zhí)行創(chuàng)建日志消息所有必需的步驟唧取,在不知道日志框架是否將使用日志消息的前提下。因此在創(chuàng)建調(diào)試消息之前划提,最好先檢查當(dāng)前的日志級別枫弟。

// do this

if (log.isDebugEnabled()) {

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

}

10.使用Apache CommonsStringUtils.Replace而不是String.replace

一般來說,String.replace方法工作正常鹏往,效率很高淡诗,尤其是在使用Java 9的情況下。但是伊履,如果你的應(yīng)用程序需要大量的替換操作袜漩,并且沒有更新到最新的Java版本,那么我們依然有必要查找更快和更有效的替代品湾碎。

有一個(gè)備選答案是Apache Commons Lang的StringUtils.replace方法宙攻。正如Lukas Eder在他最近的一篇博客文章中所描述的,StringUtils.replace方法遠(yuǎn)勝Java 8的String.replace方法介褥。

而且它只需要很小的改動座掘。即添加Apache Commons Lang項(xiàng)目的Maven依賴項(xiàng)到應(yīng)用程序pom.xml中递惋,并將String.replace方法的所有調(diào)用替換為StringUtils.replace方法。

// replace this

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

// with this

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

為了讓學(xué)習(xí)變得輕松溢陪、高效萍虽,今天給大家免費(fèi)分享一套Java入門教學(xué)資源。幫助大家在成為Java架構(gòu)師的道路上披荊斬棘形真。需要資料的歡迎加入學(xué)習(xí)交流群:9285杉编,05736

?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咆霜,隨后出現(xiàn)的幾起案子邓馒,更是在濱河造成了極大的恐慌,老刑警劉巖蛾坯,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件光酣,死亡現(xiàn)場離奇詭異,居然都是意外死亡脉课,警方通過查閱死者的電腦和手機(jī)救军,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倘零,“玉大人唱遭,你說我怎么就攤上這事〕适唬” “怎么了拷泽?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俐东。 經(jīng)常有香客問我,道長订晌,這世上最難降的妖魔是什么虏辫? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮锈拨,結(jié)果婚禮上砌庄,老公的妹妹穿的比我還像新娘。我一直安慰自己奕枢,他們只是感情好娄昆,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缝彬,像睡著了一般萌焰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谷浅,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天扒俯,我揣著相機(jī)與錄音奶卓,去河邊找鬼。 笑死撼玄,一個(gè)胖子當(dāng)著我的面吹牛夺姑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掌猛,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼盏浙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荔茬?” 一聲冷哼從身側(cè)響起废膘,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兔院,沒想到半個(gè)月后殖卑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坊萝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年孵稽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十偶。...
    茶點(diǎn)故事閱讀 40,912評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菩鲜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惦积,到底是詐尸還是另有隱情接校,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布狮崩,位于F島的核電站蛛勉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏睦柴。R本人自食惡果不足惜诽凌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坦敌。 院中可真熱鬧侣诵,春花似錦、人聲如沸狱窘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蘸炸。三九已至躬络,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搭儒,已是汗流浹背洗鸵。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工越锈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膘滨。 一個(gè)月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓甘凭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親火邓。 傳聞我的和親對象是個(gè)殘疾皇子丹弱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評論 2 361

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