• <track id="yjx63"><span id="yjx63"></span></track>
  • <track id="yjx63"></track>

  • 衡水天泰計算機培訓學校
    JAVA技術
    當前位置:首頁 > 學習專區 > JAVA技術 >

    Java程序性能調優步驟

    來源:衡水北大青鳥 ????發布時間:2017-04-11 10:50 ????點擊:

    1、性能調優的步驟
     
    1.1、衡量系統現狀
     
    包括請求次數、響應時間、資源消耗等;如:A系統目前95%的請求響應為1s。
     
    1.2、設定調優目標
     
    根據用戶所能接受的響應速度、系統現有的機器、所支撐的用戶量制定出來的,因此通常會設定調優目標:95%的 請求在500ms內返回。
     
    1.3、尋找性能瓶頸
     
    在【2、尋找性能瓶頸】會專門介紹。通常性能瓶頸的表像是:
     
    1.3.1、資源消耗過多(CPU、文件IO、網絡IO、內存)
     
    1.3.2、外部系統處理不足(所調用的其他系統提供的功能——多數情況也是資源消耗過多、數據的操作響應速度 不夠——根據數據庫SQL執行速度、數據庫機器的IOPS、數據庫的Active Sessions等分析出來的)
     
    1.3.3、程序代碼運行效率不夠高,未充分使用資源或程序結構不合理。
     
    1.4、性能調優
     
    在后面的【3、性能調優】會專門介紹
     
    1.5、衡量是否達到目標值
     
    優化部署后,達到目標則結束,如果沒有則重復1.3、1.4步驟
     
    2、尋找性能瓶頸
     
    2.1、CPU消耗分析(top、pidstat等方式查看cpu消耗狀況;vmstat查看cpu的上下文切換、運行隊列、利用率)
     
    在Linux中,CPU消耗主要用于中斷、內核以及用戶進程的任務處理。優先級為:中斷>內核>用戶進程。cpu消耗嚴重時,主要體現在:
     
    ①us—用戶進程所占的%
     
    過高的原因:1、線程一直處于可運行(Runnable)狀態,通常線程在執行無阻塞、循環、正則或純粹的計算等動作引起的。2、頻繁的GC操作引起。如:每次請求需要分配較多內存,當訪問量高的時,就不斷的進行GC,系統響應速度下降。進而造成堆積的請求更多,消耗內存更嚴重,最嚴重的時候可能導致系統 不斷的進行FULL GC??赏ㄟ^JVM內存的消耗分析來查找原因。
     
    可通過kill -3 [javapid]、jstack [pid] | grep 'nid=0X....' 的方式dump出應用的java線程信息。通過轉換出的十六進制的值就可以找到對應的nid值的線程。該線程即為消耗CPU的線程。在采樣時多執行幾次上訴過程,以確保找到真實的消耗CPU的線程。也可以通過intel vtune 這樣的商業軟件進行分析
     
    ②sy—內核線程所占的%
     
    過高的原因:Linux花費更多的時間在進行線程切換。Java應用造成這個原因是:因為啟動了的線程比較多,且這些線程多數都處于不斷的阻塞(鎖等待、IO等待狀態)和執行狀態的變化過程,導致了操作系統需要不斷的切換執行的線程。從而產生大量的上下文切換。
     
    可通過kill -3 [javapid]、jstack -1 [javapid] 的方式dump出Java應用線程信息,查看線程的狀態、鎖信息找出等待狀態或鎖競爭過多的線程。結合vvmstat 查看CPU消耗狀況。如cs(上下文切換)、sy等。
     
    ③ni—被nice命令改變優先級的任務所占的%
     
    ④id—CPU空閑時間所占的%
     
    ⑤wa—執行過程中等待io所占的%
     
    ⑥hi—硬件中斷所占的%
     
    ⑦si—軟件中斷所占的%
     
    2.2、文件IO消耗分析(通過pidstat、iostat命令分析)
     
    Java應用造成io消耗嚴重主要是:
     
    ① 多個線程需要大量內容寫入(如頻繁的log寫入)動作;
     
    ② 磁盤設備本身的處理速度慢
     
    ③ 文件系統慢
     
    ④ 操作的文件本身已經很大
     
    2.3、網絡IO消耗分析(通過sar命令,如需跟著TCP/IP通信過程的信息,則可通過tcpdump來進行)
     
    對于分布式Java應用而言,網絡IO的消耗非常值得關注,尤其要注意網絡中斷是不是均衡地分配到各CPU的(通過cat/proc/interrupts命令查看)。對于網卡只分配到一個CPU的現象采用修改kernle方法(Google使用)、 采用支持MSI-X的網卡進行修復。
     
    由于沒辦法分析具體每個線程所消耗的網絡IO,因此當網絡IO消耗高時,對于Java應用而言只能對線程進dump。
     
    查找產生大量網絡IO操作的線程,這些線程的特征是讀取或寫入網絡流,在Java網絡通信時,通常要對對象進行序列
     
    化為字節流,進行發送,或者讀取。并反序列化為對象。這個過程要消耗JVM堆內存,JVM對內存通常是有限的。因此,Java應用一般不會造成網絡IO消耗嚴重。
     
    2.4、內存消耗分析(vmstat、sar、pidstat、top)
     
    目前Java應用只有在創建線程和使用Direct ByteBuffer時才會操作JVM堆意外的內存。對于JVM堆以外的內存方面消耗,最為值得關注的是swpd的消耗以及物理內存的消耗(可通過vmstat、sar、top、pidstat等方式查看swap和物理內存的消耗狀況)。
     
    2.5、消耗資源不多,在訪問量不大的情況。但程序執行慢的原因,主要有3方面
     
    ① 鎖競爭激烈(如:數據庫連接池數,但是請求數多于連接池數)
     
    ② 未充分使用硬件資源(如:多核CPU,但程序卻采用單線程串行操作。)
     
    ③ 數據量增長(如:數據量的海量增長??蓪祿斓谋聿鸱?、庫拆分)
     
    3、性能調優
     
    3.1JVM調優
     
    3.1.1、代大小調優
     
    ① 避免新生代大小設置過小
     
    1、避免頻繁進行minor GC;2、可能導致minor GC對象直接進入舊生代,占據舊生代空間,觸發FULL GC。
     
    ② 避免新生代設置過大
     
    1、導致舊生代變小,可能導致FULL GC頻繁執行;2、導致minor GC的耗時大幅度增加。
     
    ③ 避免survivor space過小或者過大
     
    ④ 根據具體代碼合理設置新生代的存活周期。
     
    3.2.1、GC策略調優
     
    串行GC性能太差,因此實際應用時主要是應用并行和并發GC,大部分Web應用在處理請求時設置了一個最大可同時處理的請求數,當超出此請求數時,會將之后的請求放 入等待隊列中,而這個等待隊列也限制了大小。當等待隊列滿了后,仍然有請求進入,那么這些請求將丟棄,所有的請求又都是有超時限制度。
     
    在這種情況下如果觸發了FULL GC造成應用暫停時間較長的FULL GC,則有可能等這次FULL GC之后,應用中很多請求就超時或者被丟棄了。
     
    從上面可以看出,Web應用非常需要一個對應用造成暫停時間較短的GC,再加上大部分Web應用的瓶頸都不在CPU上。因此對于Web應用而言,在G1還不夠成熟的情況下,CMS GC是不錯的選擇。
     
    3.2、程序調優
     
    3.2.1、CPU us高的解決方法
     
    ① 執行線程無任何掛起動作,且一直運行,導致CPU沒有機會去調度執行其他的線程,造成線程餓死的現象。
     
    解決:對這種線程的動作增加Thread.sleep(int),以釋放CPU的執行權,降低CPU的消耗。
     
    原理:以損失單次執行性能為代價,但由于降低了CPU消耗,在多線程的情況下,反而提高了平均性能。
     
    ② 狀態掃描。如:某線程要等其他線程改變了值才可以繼續執行。
     
    解決:改為采用wait/notify機制。
     
    ③ 循環次數過多、正則、計算等造成CPU us過高的情況。結合業務需求進行調優。code review是王道。
     
    ④ 頻繁GC造成us高的情況,通過JVM調優或程序調優,降低GC的執行次數。
     
    3.2.2、CPU sy高的解決方法
     
    ① 線程運行狀態經常切換
     
    解決:減少線程數,且使用線程池
     
    ② 線程之間鎖競爭激烈
     
    解決:盡可能降低鎖的競爭。
     
    1、使用并發包中的類(java.util.concurrent.*)
     
    2、使用Treiber算法
     
    3、使用Michael-Scott非阻塞隊列算法(ConcurrentLinkedQueue就是典型的該算法的非阻塞隊列)
     
    4、通常沒必要對整個方法加鎖,只對需要控制資源的地方做加鎖操作。
     
    5、拆分鎖,把獨占鎖拆分為多把鎖,如:ConcurrentHashMap。很大程度上可以提高讀寫速度。
     
    6、去除讀寫操作的互斥鎖
     
    ③ 較多網絡IO操作或者確實需要一些鎖競爭機制(如數據庫連接池),但為了能夠支持高的并發量,在Java應用中又只能借助更多的線程來支撐。
     
    解決:采用協程(Coroutine)來支持更高的并發量,避免并發量上漲之后造成CPU sy消耗嚴重、系統load迅速上漲和系統性能下降。
     
    Java中目前主要可用于實現協程的框架為Kilim,早使用Kilim執行一項任務,并不創建Thread,而是采用Task。
     
    3.3、文件IO消耗嚴重的解決方法
     
    從程序角度看,造成文件IO消耗嚴重的原因主要是多個線程在寫大量的數據到同一文件。導致文件很快變大。
     
    從而寫入速度越來越慢,并造成各線程激烈爭搶文件鎖,對于這種情況解決方法:
     
    1、異步寫入文件;2、批量讀寫;3、限流;4、限制文件大小;5、盡可能采用緩沖區等方式來讀取文件內容
     
    3.4、網絡IO消耗嚴重的解決方法
     
    從程序角度而言,造成網絡IO消耗嚴重的主要原因是同時需要發送或接受的包太多??刹捎孟蘖?。限流通常是限制發送packet的頻率,從而在網絡IO消耗可接受的情況下來發送packet。
     
    3.5、內存消耗嚴重的情況
     
    1、對JVM調優;2、代碼調優;
     
    代碼調優的方式:
     
    ① 釋放不必要的引用。如使用ThreadLocal,由于線程復用,ThreadLocal中存放的對象如未主動釋放的話,不會被GC。應該在執行完畢執行ThreadLocal.set把對象清除,避免此有不必要的對象引用。
     
    ② 使用對象緩存池(享元模式)
     
    ③ 采用合理的緩存失效算法(FIFO、LRU、LFU等)
     
    當緩存池達到最大容量后,如果再加入新對象時采用FIFO、LRU、LFU等失效算法。
     
    ④ 對于占據內存但又不是必須存在的對象使用SoftReference、WeakReference的方式進行緩存。
     
    SoftReference在內存不夠用的情況進行回收;WeakReference在FULL GC的情況下進行回收。
     
    3.6、對于資源消耗不多,但程序執行慢的情況
     
    3.6.1、鎖競爭激烈—見3.2.2②
     
    3.6.2、未充分利用硬件資源。
     
    ① 未充分利用CPU
     
    啟動多線程,但是單線程演變為多線程要加鎖,如:單線程計算,拆分為多線程分別計算,最后合并結果 如:JDK7的fork-join框架。
     
    ② 未充分使用內存
     
    數據庫緩存、耗時資源緩存(數據庫連接的創建、網絡連接的創建等)、頁面片段的緩存等。
     
    結束語:從純粹的軟件角度調優來講,充分而不過分的使用硬件資源,合理調整JVM以及合理使用JDK包是調優的三大有效原則,調優沒有“銀彈”。結合系統現狀和多嘗試不同的調優策略是找到合適調優方法的唯一途徑。
    掃一掃 加關注 精彩資訊在這里!

    技能認證

    OSTA技能證書,全國可查,真實可信

    多項熱門專業自由選擇

    豐富的專業課程設置,完全自由選擇

    畢業推薦就業

    5重就業保障體系,4項就業指導服務

    學校地址

    最方便的乘車路線

    在線咨詢

    來校講解答疑

    在線咨詢

    399入學禮包

    報名熱線

    0318-2888080

    • 版權所有2015~2021 衡水天泰計算機培訓學校
      北大青鳥課程咨詢電話:0318-2888080 官方網址:www.lv2012gf.com
      地址:河北省衡水市育才街與勝利交叉口南行200米路東 郵編:053000
      (網站客服咨詢QQ:3142643279)
    • 網站備案/許可證號:冀ICP備19035316號-1
    日韩无码国产精品97精品|国产人妻无码一区二区三区动态|欧美97欧美色伦综合视频在线看|人妻少妇中文字幕乱码高清|

  • <track id="yjx63"><span id="yjx63"></span></track>
  • <track id="yjx63"></track>