posts - 10,comments - 2,trackbacks - 0
          1、性能調(diào)優(yōu)的步驟
              1.1、衡量系統(tǒng)現(xiàn)狀
                  包括請求次數(shù)、響應(yīng)時(shí)間、資源消耗等;如:A系統(tǒng)目前95%的請求響應(yīng)為1s。

              1.2、設(shè)定調(diào)優(yōu)目標(biāo)
                  根據(jù)用戶所能接受的響應(yīng)速度、系統(tǒng)現(xiàn)有的機(jī)器、所支撐的用戶量制定出來的,因此通常會(huì)設(shè)定調(diào)優(yōu)目標(biāo):95%的      請求在500ms內(nèi)返回。

              1.3、尋找性能瓶頸
                  在【2、尋找性能瓶頸】會(huì)專門介紹。通常性能瓶頸的表像是:
                  1.3.1、資源消耗過多(CPU、文件IO、網(wǎng)絡(luò)IO、內(nèi)存)
                  1.3.2、外部系統(tǒng)處理不足(所調(diào)用的其他系統(tǒng)提供的功能——多數(shù)情況也是資源消耗過多、數(shù)據(jù)的操作響應(yīng)速度               不夠——根據(jù)數(shù)據(jù)庫SQL執(zhí)行速度、數(shù)據(jù)庫機(jī)器的IOPS、數(shù)據(jù)庫的Active Sessions等分析出來的)
                  1.3.3、程序代碼運(yùn)行效率不夠高,未充分使用資源或程序結(jié)構(gòu)不合理。

              1.4、性能調(diào)優(yōu)
                  在后面的【3、性能調(diào)優(yōu)】會(huì)專門介紹

              1.5、衡量是否達(dá)到目標(biāo)值
                  優(yōu)化部署后,達(dá)到目標(biāo)則結(jié)束,如果沒有則重復(fù)1.3、1.4步驟

          2、尋找性能瓶頸

              2.1、CPU消耗分析(top、pidstat等方式查看cpu消耗狀況;vmstat查看cpu的上下文切換、運(yùn)行隊(duì)列、利用率)
                  在Linux中,CPU消耗主要用于中斷、內(nèi)核以及用戶進(jìn)程的任務(wù)處理。優(yōu)先級(jí)為:中斷>內(nèi)核>用戶進(jìn)程。cpu消耗
              嚴(yán)重時(shí),主要體現(xiàn)在:
                  ①us—用戶進(jìn)程所占的%
                      過高的原因:1、線程一直處于可運(yùn)行(Runnable)狀態(tài),通常線程在執(zhí)行無阻塞、循環(huán)、正則或純粹的計(jì)算
                                          等動(dòng)作引起的。
                                       2、頻繁的GC操作引起。如:每次請求需要分配較多內(nèi)存,當(dāng)訪問量高的時(shí),就不斷的進(jìn)行GC,
                                          系統(tǒng)響應(yīng)速度下降。進(jìn)而造成堆積的請求更多,消耗內(nèi)存更嚴(yán)重,最嚴(yán)重的時(shí)候可能導(dǎo)致系統(tǒng)                                   不斷的進(jìn)行FULL GC。可通過JVM內(nèi)存的消耗分析來查找原因。
                      可通過kill -3 [javapid]、jstack [pid] | grep 'nid=0X....'的方式dump出應(yīng)用的java線程信息。通過轉(zhuǎn)換
                  出的十六進(jìn)制的值就可以找到對應(yīng)的nid值的線程。該線程即為消耗CPU的線程。在采樣時(shí)多執(zhí)行幾次上訴過程,
                  以確保找到真實(shí)的消耗CPU的線程。也可以通過intel vtune這樣的商業(yè)軟件進(jìn)行分析

                  ②sy—內(nèi)核線程所占的%
                      過高的原因:Linux花費(fèi)更多的時(shí)間在進(jìn)行線程切換。Java應(yīng)用造成這個(gè)原因是:因?yàn)閱?dòng)了的線程比較多,且
                  這些線程多數(shù)都處于不斷的阻塞(鎖等待、IO等待狀態(tài))和執(zhí)行狀態(tài)的變化過程,導(dǎo)致了操作系統(tǒng)需要不斷的切換
                  執(zhí)行的線程。從而產(chǎn)生大量的上下文切換。
                      
                      可通過kill -3 [javapid]、jstack -1 [javapid]的方式dump出Java應(yīng)用線程信息,查看線程的狀態(tài)、鎖信息,
                  找出等待狀態(tài)或鎖競爭過多的線程。結(jié)合vvmstat查看CPU消耗狀況。如cs(上下文切換)、sy等。

                  ③ni—被nice命令改變優(yōu)先級(jí)的任務(wù)所占的%
                  ④id—CPU空閑時(shí)間所占的%
                  ⑤wa—執(zhí)行過程中等待io所占的%
                  ⑥hi—硬件中斷所占的%
                  ⑦si—軟件中斷所占的%

              2.2、文件IO消耗分析(通過pidstat、iostat命令分析)
                  Java應(yīng)用造成io消耗嚴(yán)重主要是:
                      ① 多個(gè)線程需要大量內(nèi)容寫入(如頻繁的log寫入)動(dòng)作;
                      ② 磁盤設(shè)備本身的處理速度慢
                      ③ 文件系統(tǒng)慢
                      ④ 操作的文件本身已經(jīng)很大

              2.3、網(wǎng)絡(luò)IO消耗分析(通過sar命令,如需跟著TCP/IP通信過程的信息,則可通過tcpdump來進(jìn)行)
                      對于分布式Java應(yīng)用而言,網(wǎng)絡(luò)IO的消耗非常值得關(guān)注,尤其要注意網(wǎng)絡(luò)中斷是不是均衡地分配到各CPU的
              (通過cat/proc/interrupts命令查看)。對于網(wǎng)卡只分配到一個(gè)CPU的現(xiàn)象采用修改kernle方法(Google使用)、       采用支持MSI-X的網(wǎng)卡進(jìn)行修復(fù)。
                  由于沒辦法分析具體每個(gè)線程所消耗的網(wǎng)絡(luò)IO,因此當(dāng)網(wǎng)絡(luò)IO消耗高時(shí),對于Java應(yīng)用而言只能對線程進(jìn)dump。
              查找產(chǎn)生大量網(wǎng)絡(luò)IO操作的線程,這些線程的特征是讀取或?qū)懭刖W(wǎng)絡(luò)流,在Java網(wǎng)絡(luò)通信時(shí),通常要對對象進(jìn)行序列
              化為字節(jié)流,進(jìn)行發(fā)送,或者讀取。并反序列化為對象。這個(gè)過程要消耗JVM堆內(nèi)存,JVM對內(nèi)存通常是有限的。因此
              Java應(yīng)用一般不會(huì)造成網(wǎng)絡(luò)IO消耗嚴(yán)重。

              2.4、內(nèi)存消耗分析(vmstat、sar、pidstat、top)
                  目前Java應(yīng)用只有在創(chuàng)建線程和使用Direct ByteBuffer時(shí)才會(huì)操作JVM堆意外的內(nèi)存。對于JVM堆以外的內(nèi)存方
              面消耗,最為值得關(guān)注的是swpd的消耗以及物理內(nèi)存的消耗(可通過vmstat、sar、top、pidstat等方式查看swap
              和物理內(nèi)存的消耗狀況)。

              2.5、消耗資源不多,在訪問量不大的情況。但程序執(zhí)行慢的原因,主要有3方面
                  ① 鎖競爭激烈(如:數(shù)據(jù)庫連接池?cái)?shù),但是請求數(shù)多于連接池?cái)?shù))
                  ② 未充分使用硬件資源(如:多核CPU,但程序卻采用單線程串行操作。)
                  ③ 數(shù)據(jù)量增長(如:數(shù)據(jù)量的海量增長。可對數(shù)據(jù)庫的表拆分、庫拆分)


          3、性能調(diào)優(yōu)
              3.1JVM調(diào)優(yōu)

                  3.1.1、代大小調(diào)優(yōu)
                      ① 避免新生代大小設(shè)置過小
                      1、避免頻繁進(jìn)行minor GC;2、可能導(dǎo)致minor GC對象直接進(jìn)入舊生代,占據(jù)舊生代空間,觸發(fā)FULL GC。
                      ② 避免新生代設(shè)置過大
                      1、導(dǎo)致舊生代變小,可能導(dǎo)致FULL GC頻繁執(zhí)行;2、導(dǎo)致minor GC的耗時(shí)大幅度增加。
                      ③ 避免survivor space過小或者過大            
                      ④ 根據(jù)具體代碼合理設(shè)置新生代的存活周期。

                  3.1.1、GC策略調(diào)優(yōu)
                          串行GC性能太差,因此實(shí)際應(yīng)用時(shí)主要是應(yīng)用并行和并發(fā)GC,
                          大部分Web應(yīng)用在處理請求時(shí)設(shè)置了一個(gè)最大可同時(shí)處理的請求數(shù),當(dāng)超出此請求數(shù)時(shí),會(huì)將之后的請求放             入等待隊(duì)列中,而這個(gè)等待隊(duì)列也限制了大小。當(dāng)?shù)却?duì)列滿了后,仍然有請求進(jìn)入,那么這些請求將丟棄,所
                      有的請求又都是有超時(shí)限制度。
                          在這種情況下如果觸發(fā)了FULL GC造成應(yīng)用暫停時(shí)間較長的FULL GC,則有可能等這次FULL GC之后,應(yīng)
                      用中很多請求就超時(shí)或者被丟棄了。
                          從上面可以看出,Web應(yīng)用非常需要一個(gè)對應(yīng)用造成暫停時(shí)間較短的GC,再加上大部分Web應(yīng)用的瓶頸都
                      不在CPU上。因此對于Web應(yīng)用而言,在G1還不夠成熟的情況下,CMS GC是不錯(cuò)的選擇。
              
              3.2、程序調(diào)優(yōu)
                  3.2.1、CPU us高的解決方法
                      ① 執(zhí)行線程無任何掛起動(dòng)作,且一直運(yùn)行,導(dǎo)致CPU沒有機(jī)會(huì)去調(diào)度執(zhí)行其他的線程,造成線程餓死的現(xiàn)象。
                          解決:對這種線程的動(dòng)作增加Thread.sleep(int),以釋放CPU的執(zhí)行權(quán),降低CPU的消耗。
                          原理:以損失單次執(zhí)行性能為代價(jià),但由于降低了CPU消耗,在多線程的情況下,反而提高了平均性能。

                      ② 狀態(tài)掃描。如:某線程要等其他線程改變了值才可以繼續(xù)執(zhí)行。
                          解決:改為采用wait/notify機(jī)制。

                      ③ 循環(huán)次數(shù)過多、正則、計(jì)算等造成CPU us過高的情況。結(jié)合業(yè)務(wù)需求進(jìn)行調(diào)優(yōu)。code review是王道。

                      ④ 頻繁GC造成us高的情況,通過JVM調(diào)優(yōu)或程序調(diào)優(yōu),降低GC的執(zhí)行次數(shù)。
                  
                  3.2.2、CPU sy高的解決方法
                      ① 線程運(yùn)行狀態(tài)經(jīng)常切換
                          解決:減少線程數(shù),且使用線程池
                      
                      ② 線程之間鎖競爭激烈
                          解決:盡可能降低鎖的競爭。
                              1、使用并發(fā)包中的類(java.util.concurrent.*)
                              2、使用Treiber算法
                              3、使用Michael-Scott非阻塞隊(duì)列算法(ConcurrentLinkedQueue就是典型的該算法的非阻塞隊(duì)列)
                              4、通常沒必要對整個(gè)方法加鎖,只對需要控制資源的地方做加鎖操作。
                              5、拆分鎖,把獨(dú)占鎖拆分為多把鎖,如:ConcurrentHashMap。很大程度上可以提高讀寫速度。
                              6、去除讀寫操作的互斥鎖

                      ③ 較多網(wǎng)絡(luò)IO操作或者確實(shí)需要一些鎖競爭機(jī)制(如數(shù)據(jù)庫連接池),但為了能夠支持高的并發(fā)量,在Java
                      應(yīng)用中又只能借助更多的線程來支撐。
                          解決:采用協(xié)程(Coroutine)來支持更高的并發(fā)量,避免并發(fā)量上漲之后造成CPU sy消耗嚴(yán)重、系統(tǒng)load
                      迅速上漲和系統(tǒng)性能下降。
                          Java中目前主要可用于實(shí)現(xiàn)協(xié)程的框架為Kilim,早使用Kilim執(zhí)行一項(xiàng)任務(wù),并不創(chuàng)建Thread,而是采用
                      Task。

              3.3、文件IO消耗嚴(yán)重的解決方法
                      從程序角度看,造成文件IO消耗嚴(yán)重的原因主要是多個(gè)線程在寫大量的數(shù)據(jù)到同一文件。導(dǎo)致文件很快變大。
                  從而寫入速度越來越慢,并造成各線程激烈爭搶文件鎖,對于這種情況解決方法:
                      1、異步寫入文件;2、批量讀寫;3、限流;4、限制文件大小;5、盡可能采用緩沖區(qū)等方式來讀取文件內(nèi)容
              
              3.4、網(wǎng)絡(luò)IO消耗嚴(yán)重的解決方法
                      從程序角度而言,造成網(wǎng)絡(luò)IO消耗嚴(yán)重的主要原因是同時(shí)需要發(fā)送或接受的包太多。可采用限流。限流通常是
                  限制發(fā)送packet的頻率,從而在網(wǎng)絡(luò)IO消耗可接受的情況下來發(fā)送packet。

              3.5、內(nèi)存消耗嚴(yán)重的情況
                      1、對JVM調(diào)優(yōu);2、代碼調(diào)優(yōu);
                      代碼調(diào)優(yōu)的方式:
                      ① 釋放不必要的引用。如使用ThreadLocal,由于線程復(fù)用,ThreadLocal中存放的對象如未主動(dòng)釋放的話,
                  不會(huì)被GC。應(yīng)該在執(zhí)行完畢執(zhí)行ThreadLocal.set把對象清除,避免此有不必要的對象引用。

                      ② 使用對象緩存池(享元模式)

                      ③ 采用合理的緩存失效算法(FIFO、LRU、LFU等)
                         當(dāng)緩存池達(dá)到最大容量后,如果再加入新對象時(shí)采用FIFO、LRU、LFU等失效算法。
                      ④ 對于占據(jù)內(nèi)存但又不是必須存在的對象使用SoftReference、WeakReference的方式進(jìn)行緩存。
                          SoftReference在內(nèi)存不夠用的情況進(jìn)行回收;WeakReference在FULL GC的情況下進(jìn)行回收。

              3.6、對于資源消耗不多,但程序執(zhí)行慢的情況
                  3.6.1、鎖競爭激烈—見3.2.2②
                  
                  3.6.2、未充分利用硬件資源。
                      ① 未充分利用CPU
                          啟動(dòng)多線程,但是單線程演變?yōu)槎嗑€程要加鎖,如:單線程計(jì)算,拆分為多線程分別計(jì)算,最后合并結(jié)果                 如:JDK7的fork-join框架。

                      ② 未充分使用內(nèi)存
                          數(shù)據(jù)庫緩存、耗時(shí)資源緩存(數(shù)據(jù)庫連接的創(chuàng)建、網(wǎng)絡(luò)連接的創(chuàng)建等)、頁面片段的緩存等。

              結(jié)束語:從純粹的軟件角度調(diào)優(yōu)來講,充分而不過分的使用硬件資源,合理調(diào)整JVM以及合理使用JDK包是調(diào)優(yōu)的三大有效原則,調(diào)優(yōu)沒有“銀彈”。結(jié)合系統(tǒng)現(xiàn)狀和多嘗試不同的調(diào)優(yōu)策略是找到合適調(diào)優(yōu)方法的唯一途徑。
          posted on 2011-09-01 23:27 showsun 閱讀(792) 評(píng)論(0)  編輯  收藏 所屬分類: J2EEWeb
          主站蜘蛛池模板: 沙洋县| 商洛市| 佳木斯市| 南澳县| 陇川县| 定安县| 镇沅| 湖南省| 高雄市| 夏津县| 丹凤县| 黔西| 察雅县| 武乡县| 泰宁县| 上杭县| 浮梁县| 安泽县| 永清县| 酉阳| 周至县| 西昌市| 随州市| 垣曲县| 潞西市| 治县。| 高邑县| 天柱县| 内江市| 林甸县| 水城县| 金华市| 凤冈县| 拜泉县| 贺州市| 东港市| 长兴县| 苍溪县| 贵港市| 屯门区| 庄河市|