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使用率等)。
1、吞吐量/率:
目標—是否可以比設定的吞吐量低?如果可以,這種狀態可以持續多久?最低可以低到多少?
測量—怎麼測量?(事務數/秒、訊息數/秒或兩種方式)哪裡測量?(客戶端、伺服器端或瀏覽器端)
2、回應時間:
目標—是否可以超出設定的回應時長?如果可以,這種狀態可以持續多久?最長可以達到多久?
測試—怎樣測量?(取99%響應的時間計算均值、只統計某一段響應時間(5-9秒)、最差的情況或全部)哪裡測量?(客戶端、伺服器端或整個環路)
3、容量管理:
可以接受的容量是多少?如果某個系統過載怎麼辦(負載平衡出現問題)?怎樣測量容量?一個系統和所有系統能承受的最大容量是多少?可以承受多久?需要監測哪些指標?
二、性能結果分析
關於性能結果分析,這裡只討論Java的性能分析。分析哪些因素會影響終端使用者體驗,無法達到預期的QoS;追蹤監測效能指標。下圖是一個分層狀況圖:
應用層生態系統:應用服務、應用伺服器、資料庫、生態系統中其他服務
JRE層:類別載入情況、JIT編譯情況、垃圾回收狀況、執行緒狀況
作業系統層:系統/核心狀態、鎖定狀態、執行緒狀態
硬體層:記憶體頻寬/記憶體吞吐量/記憶體佔用、CPU/核心的使用、CPU快取效率/使用/等級、處理器結構、 IO狀態
效能調優的執行
兩種實作模式
Monica提出了兩種實作方式:自上而下、自下而上。採用哪一種要看你想要實現什麼。
如果想從應用層面入手進行改進的話,且你是一個應用程式工程師,具備修改程式碼的能力,可以採用自上而下的方式。
如果想從平台層面入手進行改進的話,可採用自下而上的方式。首先你要明確平台的哪個模組是需要改進的;其次列出相關的應用、進行工作量的評估;然後再尋找恰當的工具。
四個步驟
不論哪一種方向,均可分為四步:第一步監控、第二步歸納、第三步分析、第四步調優、應用。
通常而言,關心的指標有以下幾個。
CPU:CPU狀態、核心狀態、快取命中和沒有命中的次數、分支預測、管線、條件轉移、load-store的工作模式等
記憶體:記憶體使用、記憶體、頻寬、讀寫狀態、讀取操作的最大頻寬、寫入操作的最大頻寬、最大容量、與結構相關的。
JVM/GC:收集與變更相關的資訊、收集一般或併發的GC各階段的資訊、並發工作佇列和工作狀態、內部佇列或快取等。
1、監控
首先是從監控環節做起。
監控方式分為三種:主動(警報設定)、被動(網路分流器)、離線(日誌抓取)。
🎜可以選用的工具有三類:🎜第三方——VisualVM、Java Flight Recorder
JVM自帶命令——PrintCompilation、PrintGCDetails(+PrintGCDateStamps)、jmap-clstats、jcmd GC.class-stats
作業系統自帶——Linux下面有mpstat、statisbt – iostat、pidstat、prstat、vmstat、dash、CPU – Z、 cacti等;Windows下面有Performance Monitor、Task Manager、Resource Monitor、CPU-Z、cacti等
2、歸納和分析
接下來是歸納和分析環節。
這時候你已經有了所有需要的信息,你需要辨識出哪些地方需要提升,分析出哪些是潛在需要改進的問題。這個環節可以使用的開源工具有兩類:
第三方效能分析工具——Oracle Solaris Studio Performance Analyzer、perftools、PAPI、Code XL、 Dtrace、Oprofile、gprof、LTT
Java 程式層面——Visual VM、 Netbeans Profiler、JConsole
3、調優
最後一步調優。 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是怎樣工作的。佔用資源的不是過期的對象,而是存活的對象;活的對象需要去維護。在程式設計的時候要明白物件的創立、保留策略還有垃圾回收器是怎麼運作的。能考慮到這三點就很好了,你沒有必要強迫自己把每件事情都做對,只要整體可以協調妥帖就很好了。