《Java應(yīng)用程序性能調(diào)優(yōu)技術(shù)》要點:
本文介紹了Java應(yīng)用程序性能調(diào)優(yōu)技術(shù),希望對您有用。如果有疑問,可以聯(lián)系我們。
Java應(yīng)用程序,不管他們部署到的應(yīng)用程序服務(wù)器,往往會遇到同樣的問題集.作為Java EE調(diào)諧器,我已經(jīng)裸露于各種環(huán)境,并對常見問題提出了一些意見.在這方面,我看到我的角色與汽車修理師的角色相似:你告訴你的技工,引擎是啁啾的; 那么他會問你一系列問題,指導(dǎo)你量化啁啾的性質(zhì),位置和情況.從這些信息中,他形成了一個關(guān)于問題的一些可能原因的好主意.
我會分享我在現(xiàn)場遇到的一些常見問題及其癥狀.這些步調(diào)將使我們能夠使代碼運行順利并避免性能問題.
1)使用StringBuilder
這應(yīng)該是幾乎所有Java代碼中的默認(rèn)值.盡量避免+運算符.當(dāng)然,你可能會認(rèn)為這只是StringBuilder的語法糖,如下所示:
String x = “a” + args.length + “b” ;
...編譯
0 新 的java .lang .StringBuilder [16]3 DUP4 LDC < 字符串 “ 一 ”> [18]6 invokespecial 的java .lang .StringBuilder(java的.lang .String)[20]9 aload_0 [參數(shù)] 10 arraylength 11 invokevirtual 的java .lang .StringBuilder .append(INT):的java .lang .StringBuilder [23] 14 LDC < 字符串 “ b ”> [27] 16 invokevirtual 的java .lang .StringBuilder .append(java的.lang .String):java的.lang . StringBuilder的 [29] 19 invokevirtual 的java .lang .StringBuilder 的ToString():的java .lang .String [32] 22 astore_1 [X]
但是,如果以后,您必要使用可選部分修改您的String?
String x = “a” + args.length + “b” ; if(args.length == 1)x = x + args [ 0 ];
現(xiàn)在你將有一秒鐘 StringBuilder
,只是不需要地消耗你的堆的記憶,給你的GC施加壓力.寫這個:
StringBuilder x = newStringBuilder(“a”);X.append(args.length);X.append(“b”);if(args.length == 1);X.append(args [ 0 ]);
在上面的例子中,如果你使用了顯式的StringBuilder實例,或者你依靠Java編譯器為你創(chuàng)立隱式實例,這可能是完全不相干的.但請記住,我們在NOPE分行.每個CPU周期,我們都浪費在像GC那樣愚蠢的東西,或者分配一個StringBuilder的默認(rèn)容量,我們浪費了N x O x P次.
根據(jù)經(jīng)驗,總是使用StringBuilder而不是+運算符.如果可以,請保持StringBuilder引用跨多個辦法,如果您的String更復(fù)雜的構(gòu)建.
為了大聲哭泣,如果還有StringBuffer引用,請用StringBuilder替換它們.你真的幾乎不必要在正在創(chuàng)建的字符串上進(jìn)行同步.
2)內(nèi)存不敷錯誤
困擾企業(yè)應(yīng)用程序的最常見問題之一是可怕的OutOfMemoryError.差錯通常是以下之一:
應(yīng)用程序服務(wù)器瓦解.
性能下降.
一個似乎無休止的重復(fù)垃圾回收循環(huán)幾乎停止處理,通常會導(dǎo)致應(yīng)用程序服務(wù)器瓦解.
無論癥狀如何,在性能恢復(fù)正常之前,您很可能必要重新啟動應(yīng)用程序服務(wù)器.
內(nèi)存不敷錯誤的原因
在嘗試辦理內(nèi)存不足錯誤之前,首先了解如何發(fā)生錯誤是有益的.如果JVM在其進(jìn)程內(nèi)存空間(包括堆中的所有區(qū)域)以及永久內(nèi)存空間中的內(nèi)存不足,并且進(jìn)程嘗試創(chuàng)建新的對象實例,則會執(zhí)行垃圾回收器以嘗試釋放足夠的內(nèi)存允許新對象的創(chuàng)建.如果垃圾收集器無法釋放足夠的內(nèi)存來容納新對象,那么它會拋出一個OutOfMemoryError.
內(nèi)存不足錯誤最常見于Java內(nèi)存泄漏.從以前的討論中回顧一下,Java內(nèi)存泄漏是維護(hù)對未使用對象的延續(xù)引用的結(jié)果:您完成了使用對象,但是由于一個或多個其他對象仍然引用該對象,因此垃圾收集器無法回收其內(nèi)存.因此,該對象所占用的內(nèi)存從可用堆中丟失.這些類型的內(nèi)存泄漏通常發(fā)生在Web哀求期間,而一個或兩個泄露的對象可能不會使應(yīng)用程序服務(wù)器崩潰,10,000或20,000個哀求可能會發(fā)生.此外,泄漏的大多數(shù)對象不是簡單的對象,如整數(shù)或雙精度,而是代表堆內(nèi)的子圖.例如,您可能會無意中持有Person對象,并且該Person對象具有一個Profile對象,該對象具有多個PerformanceReview對象,每個對象都維護(hù)數(shù)據(jù)集.而不是丟失Person對象占用的100個字節(jié)的內(nèi)存,您將丟失可能占用500 KB或更多內(nèi)存的整個子圖.
為了識別此問題的根源,您必要確定是否存在真實的內(nèi)存泄漏,或者是否將其他內(nèi)容顯示為OutOfMemoryError.
做出這樣的決準(zhǔn)時,我使用以下兩種技巧:
闡發(fā)深度內(nèi)存統(tǒng)計信息.
反省堆的增長模式.
所有JVM(如Sun和IBM)的JVM調(diào)優(yōu)過程都不相同,但存在一些配合點.
3)避免正則表達(dá)式
正則表達(dá)式比擬便宜便宜.但是,如果您在NOPE分支機(jī)構(gòu),那么他們是最糟糕的事情.如果絕對必須在計算密集型代碼段中使用正則表達(dá)式,至少緩存“模式”引用,而不是重新編譯它們:
staticFinalPattern HEAVY_REGEX = Pattern.compile(“(((X)* Y)* Z)*”);
然則如果你的正則表達(dá)式真的很傻,就像
String [] parts = ipAddress.split(“\\”);
那么你真的更好地采用普通 char[]
或基于索引的把持.例如,這個完全不可讀的循環(huán)也是一樣的:
intlength = ipAddress.length();toffset = 0 ;intpart = 0 ;for(inti = 0 ; i < length ; i ++){if(i == length - 1 || ipAddress.charAt(i + 1)== '.'){ parts [part] = ipAddress.substring(offset,i + 1); 部分+ +; offset = i + 2 ;}}
...這也顯示了為什么你不應(yīng)該做任何過早的優(yōu)化.與split()
版原形比 ,這是不可維護(hù)的.
4)不要使用iterator()
現(xiàn)在,這個建議真的不是一般用例,而只適用于NOPE分支.不過,你應(yīng)該考慮一下.編寫Java-5風(fēng)格的foreach循環(huán)很便利.你可以完全忘記循環(huán)的內(nèi)部,并寫道:
對于(String value:strings){//在這里做一些有用的事情}
但是,每次遇到這個循環(huán)時,如果字符串是一個Iterable,您將創(chuàng)立一個新的Iterator實例.如果您使用的是ArrayList,那么將在堆上分配一個3個int的對象:
privateClassItr implementsIterator < E > { intcursor;
相反,您可以編寫以下等效循環(huán)和“浪費” int
堆棧上的單個 值,這是很廉價的:
intsize = strings.size();for(inti = 0 ; i < size ; i ++){字符串值:strings.get(i); //在這里做一些有用的事情
...或者,如果您的列表沒有真正轉(zhuǎn)變,您甚至可以在其陣列版本上操作:
對于(String value:stringArray){//在這里做一些有用的事情}
5.使用原語和客棧
上面的例子來自jOOQ,它使用了很多泛型,因此被強(qiáng)制使用包裝類型為byte,short,int和long,至少在Java 10和項目Valhalla中可以使用泛型之前.但你可能沒有這個約束在您的代碼,所以你應(yīng)該采取一切步伐替換:
//去堆 整數(shù)i = 817598 ;
… 有了這個:
//在堆棧上保存inti = 817598 ;
使用數(shù)組時,事情會變得更糟.替換為:
//三堆工具!整數(shù)[] I = { 1337,424242 };
… 有了這個:
//一個堆對象.INT [] I = { 1337,424242};
當(dāng)您深入您的NOPE分支時,您應(yīng)該非常警惕使用包裝類型.有可能你會在你的GC上造成很大的壓力,因為它必需一直踢在一起清理你的混亂.
一個特別有用的優(yōu)化可能是使用一些原始類型并創(chuàng)立它的大的一維數(shù)組,以及一些分隔符變量來指示編碼對象在數(shù)組上的位置.
一個優(yōu)秀的原始集合庫,比您的平均int []更繁雜一點,它是與LGPL一起使用的trove4j.
這個規(guī)矩有一個例外:布爾值和字節(jié)有足夠的值被JDK完全緩存.你可以寫:
Boolean a1 = true ; // ...語法糖為:Boolean a2 = Boolean .valueOf(true);字節(jié) b1 =(字節(jié))123 ; // ...語法糖為:字節(jié) b2 = 字節(jié) .valueOf((字節(jié))123);
其他整數(shù)基元類型的低值也同樣如此,包含char,short,int和long.但是,只有當(dāng)你自動打包它們,或者調(diào)用TheType.valueOf()時,不是當(dāng)你調(diào)用構(gòu)造函數(shù)時!
結(jié)論
為了有效地診斷性能問題,您需要了解問題癥狀如何映射底層問題的根本原因.如果您可以將問題分解到應(yīng)用程序代碼,那么您需要將問題轉(zhuǎn)發(fā)給應(yīng)用程序支持代理,但如果問題出在環(huán)境中,則辦理問題是在您的控制之內(nèi).
問題的根源在很大程度上取決于許多因素,但是一些指標(biāo)可以在診斷問題和完全消除其他問題時增加信心.我希望本文可以作為您的Java環(huán)境的開始故障排除指南,您可以隨著問題呈現(xiàn),自定義您的環(huán)境.
大家可以點擊參加群:606187239【JAVA大牛學(xué)習(xí)交流】
里面有Java高級大牛直播講解知識點 走的便是高端路線
(如果你想跳槽換工作 然則技術(shù)又不夠 或者工作上遇到了
瓶頸 我這里有一個JAVA的免費直播課程 講的是高端的知識點
基礎(chǔ)欠好的誤入喲 只要你有1-5年的開發(fā)經(jīng)驗
可以加群找我要講堂鏈接 注意:是免費的 沒有開發(fā)經(jīng)驗誤入哦)
維易PHP培訓(xùn)學(xué)院每天發(fā)布《Java應(yīng)用程序性能調(diào)優(yōu)技術(shù)》等實戰(zhàn)技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培養(yǎng)人才。
轉(zhuǎn)載請注明本頁網(wǎng)址:
http://www.snjht.com/jiaocheng/8004.html