Java性能調優工程的幾點建議
2016年8月,由極客邦、InfoQ和聽云聯合主辦 APMCon 2016 中國應用性能管理大會 上,Java性能調優專家Monica Beckwith進行了《Java性能調優必讀守則》(原題目:Java Performance Engineer's Survival Guide)的演講。演講中,Monica給出關于Java調優最佳實踐的個人建議:怎樣設定需要調優的性能要求、需要對哪些指標進行分析、目標設定后又怎樣具體地開展調優。
Monica Beckwith專注于企業級應用中Java虛擬機和垃圾收集器的優化,發表過多篇垃圾收集器和Java內存模型方面的文章。她之前就職于 Oracle,帶領G1垃圾收集器性能團隊,目前是一位獨立咨詢師。在Monica演講之后,InfoQ又對她進行了專訪。
性能調優前的準備工作
Monica認為性能優化工程由兩部分組成:性能需求分析及規劃、性能結果分析。兩者構成閉環,使得性能得到不斷的提升。
一、性能需求分析及規劃
在決定性能需求的時候,工程師們首先要問自己三個問題:
-
哪些會讓用戶開心?
-
哪些會讓用戶懊惱?
-
目前存在的問題需要被關注并解決嗎?
接下來,需要站在用戶的角度去思考QoS;將QoS標準量化為可測量的指標,即SLA服務等級協議;然后對SLA性能指標進行定義、梳理并排列優先級(吞吐量、響應時間、容量、請求足跡、CPU使用率等)。
-
吞吐量/率:
目標—是否可以比設定的吞吐量低?如果可以,這種狀態可以持續多久?最低可以低到多少?
測量—怎樣測量?(事務數/秒、消息數/秒或者兩種方式)哪里測量?(客戶端、服務器端或者瀏覽器端) -
響應時間:
目標—是否可以超出設定的響應時長?如果可以,這種狀態可以持續多久?最長可以達到多久?
測試—怎樣測量?(取99%響應的時間計算均值、只統計某一段響應時間(5-9秒)、最差的情況或者全部)哪里測量?(客戶端、服務器端或者整個環路)
-
容量管理:
可以接受的容量是多少?如果某個系統過載怎么辦(負載均衡出現問題)?怎樣測量容量?一個系統和所有系統能承受的最大容量是多少?可以承受多久?需要監測哪些指標?
二、性能結果分析
關于性能結果分析,這里只討論Java的性能分析。分析哪些因素會影響到終端用戶體驗,無法達到預期的QoS;跟蹤監測性能指標。下圖是一個分層情況圖:
-
應用層生態系統:應用服務、應用服務器、數據庫、生態系統中其他服務
-
JRE層:類加載情況、JIT編譯情況、垃圾回收情況、線程情況
-
操作系統層:系統/內核狀態、鎖狀態、線程狀態
-
硬件層:內存帶寬/內存吞吐量/內存占用、CPU/內核的使用、CPU緩存效率/使用/級別、處理器結構、IO狀態
性能調優的執行
兩種實現模式
Monica提出了兩種實現方式:自上而下、自下而上。采用哪一種要看你想要實現什么。
如果想從應用層面入手進行改進的話,且你是一個應用程序工程師,具備修改代碼的能力,可以采用自上而下的方式。
如果想從平臺層面入手進行改進的話,可采用自下而上的方式。首先你要明確平臺的哪個模塊是需要改進的;其次列出相關的應用、進行工作量的評估;然后再尋找恰當的工具。
四個步驟
不論哪一種方向,均可以分為四步:第一步監控、第二步歸納、第三步分析、第四步調優和應用。
通常而言,關心的指標有以下幾個。
CPU:CPU狀態、內核狀態、緩存命中和沒有命中的次數、分支預測、流水線、條件轉移、load-store的工作模式等
內存:內存使用、內存、帶寬、讀寫狀態、讀操作的最大帶寬、寫操作的最大帶寬、最大容量、與結構相關的。
JVM/GC:收集與變化相關的信息、收集一般或者并發的GC各個階段的信息、并發工作隊列和工作狀態、內部隊列或緩存等。
-
監控
首先是從監控環節做起。
監控方式分為三種:主動(報警設定)、被動(網絡分流器)、離線(日志抓取)。
可以選用的工具有三類:
第三方——VisualVM、Java Flight Recorder
JVM自帶命令——PrintCompilation、PrintGCDetails(+PrintGCDateStamps)、jmap-clstats、jcmd GC.class-stats
操作系統自帶——Linux下面有mpstat、sysstat – iostat、pidstat、prstat、vmstat、dash、CPU – Z、 cacti等;Windows下面有Performance Monitor、Task Manager、Resource Monitor、CPU-Z、cacti等
-
歸納和分析
接下來是歸納和分析環節。
這個時候你已經有了所有需要的信息,你需要辨識出哪些地方需要提升,分析出哪些是潛在需要改進的問題。這個環節可以使用的開源工具有兩類:
第三方性能分析工具——Oracle Solaris Studio Performance Analyzer、perftools、PAPI、Code XL、 Dtrace、Oprofile、gprof、LTT
Java 程序層面——Visual VM、Netbeans Profiler、JConsole
-
調優
最后一步調優。JVM/GC的調優重點在于要選擇對的堆、對的垃圾回收算法。首先正確劃分對象的所屬年代,然后只對長期存活的對象進行調優,每個虛擬機的所有GC工作線程(GC 的stop-the-world現象),同一個VM中多個 GC 線程來執行;看看壓縮普通對象指針是否有效;大的堆也許需要使能AlwaysPretouch并且將UseLargePages設置為最佳大小。此外,在代碼層面優化滿足SLA目標,設置恰當的ramp-up和ramp-down,對象的年代劃分和保留策略(理解LDS文件的形成),確保測量正確。
對話Monica
InfoQ: 在性能分析時,是否有必要收集所有的日志?
Monica:當我們知道某處需要調優時,通常來講用戶們會給我發送過來產品環境中的日志,我們基于此復制環境,然后在這個復制的環境中進行測試和檢查。我不建議在真實的環境中進行測試,因為生產環境需要保持穩定。什么情況下需要所有的日志呢?當我們確定知道某個問題的存在,比如內存泄露的問題,在這個時候就需要盡可能收集所有日志。
InfoQ: 能否分析下幾種GC方式,并做以簡單評價?
Monica:垃圾回收是Java應用調優的核心。GC不只有垃圾信息的收集、還有堆的管理分配信息;所有的分配都是類似的。一般而言,如果你有一個很小的空間可以給對象劃分世代的話;hotspot JVM中我建議在老年代對象上進行優化。因為年輕代占大多數,而且會死掉。老年代的回收算法中常見默認為垃圾標記-壓縮算法。
CMS垃圾回收器針對的是年老代的回收,從root對象開始標記存活對象。一旦空間中有不再存活的對象,所占用的資源就會被釋放,并且更新到free list中;CMS所做的就是要將年老代中所有應該歸屬于free list的都劃分其中。
另外一種設計就是G1。它將資源按區劃分,有些區域共同構成年輕代,還有一些構成年老代,即同一個世代的所有區域并不一定是鄰近的。每個區域最初都是任意的,需要通過聲明才能定義為年輕代或者年老代。在收集時期,年老代并不是一定要全部參與。G1在意的是收集有很多垃圾的區域。此外,G1還會嘗試調整年輕代的區間大小。
InfoQ: Java中現在你最想改變的是什么?
Monica:Java的非堆內存管理可能會在JDK 9 或JDK 10 中得到改善;此外檢測內存泄露很困難,我認為有許多需要提升的地方。
InfoQ: 為了實現更好的性能,你認為Java軟件開發工程師需要注意哪些事情?
Monica:在編程的時候,要想到Java GC是怎樣工作的。占用資源的并不是過期的對象,而是存活的對象;活的對象需要去維護。在編程的時候要明白對象的創立、保留策略還有垃圾回收器是怎樣工作的。能考慮到這三點就很好了,你沒有必要強制自己把每件事情都做對,只要整體可以協調妥帖就很好了。
來自:http://www.infoq.com/cn/news/2016/10/javaPerformance-guide-byMonica