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