Java程式效能優化
效能優化流程
資源使用 ---》 訪問緩慢 ---》 優化瓶頸 ---》 達到效能目標
主體就是:降低響應時間,提高吞吐量
效能指標:
-
響應時間:一次操作完成的時間。包括用於等待和服務的時間,也包括用於返回結果的時間
-
吞吐量:在資料傳輸中描述速度,例如位元組/秒,在事務系統內中,指操作的速度,每秒運算元
-
使用率:對於服務所請求的資源,描述在給定時間內的繁忙程度。例如,記憶體使用率
-
飽和度:某一資源無法滿足服務的排隊工作量。
-
錯誤:如果資源消費時發生錯誤,往往意味著出現過載情況
-
瓶頸:在系統性能中,限制系統性能的資源。識別和移除系統瓶頸是效能優化的主要工作。
響應時間和吞吐量是核心效能指標,響應時間是一次操作完成的時間,吞吐量是每秒鐘完成的事務數。
使用率、飽和度、錯誤是系統組成物件的負載描述,效能關注點按照重要程度依次是錯誤、飽和度、使用率。
瓶頸是制約系統性能的資源,效能優化的目標就是找到和優化效能瓶頸。
效能問題的描述
這個要求客戶講述自己的版本,訪問連結,同時對效能進行準確的描述,比如一個請求的響應時間等。這點非常重要。
資源的種類
硬體資源:磁碟,CPU,記憶體,快取,網路
軟體資源:檔案,程序,執行緒池,互斥鎖,連線池,執行緒
資源是有限的
獲取資源使用的應該遵守原則
-
儘可能晚地獲取資源
-
儘可能早地釋放資源
-
儘可能少地分配/訪問/移動資源
資源的分析方法
資源使用分析法
監控資源的各種指標,發現資源可能存在的瓶頸。重點關注資源的使用率、飽和度、錯誤指標。常用於總體效能問題以及特定範圍的效能問題。
時間劃片法
檢查一項工作所完成的時間,把時間劃分成小的時間段,再對最大延時時間段做再次的劃分,最後定位並量化問題的根本原因。會深入到軟體棧的各層來找到延時的原因。一個大的步驟可以進一步下鑽,分解成更細的步驟並分析優化。
在分析資源前應該瞭解一個系統的部署架構,比如一個網站接受http請求,傳送到nginx伺服器,然後分發到K8S的負載均衡器,再傳給K8S的業務容器中呼叫redis,mysql,mq等容器,處理完成後,原地返回。
常見的效能問題
慢sql,頻繁地讀取資料庫,低效的程式碼,GC,併發異常,錯誤的配置
網路分析
ifconfig命令,檢視RX,TX資訊欄
RX errors: 總收包的錯誤數量 RX dropped: 進入到Ring Buffer的幀在拷貝到記憶體的過程中被丟棄。 RX overruns: 增大意味著資料包沒到 Ring Buffer 就被網絡卡物理層給丟棄。 RX frame: 表示 misaligned 的 frames。 collisions 則表示由於 CSMA/CD 造成的傳輸中斷。
對於 TX 的來說,出現上述 counter 增大的原因主要包括 aborted transmission, errors due to carrirer, fifo error, heartbeat erros 以及 windown error,而 collisions 則表示由於 CSMA/CD 造成的傳輸中斷。
MTU: Maximum Transmit Unit,最大傳輸單元,即物理介面(資料鏈路層)提供給其上層(通常是IP層)最大一次傳輸資料的大小;以普遍使用的乙太網介面為例,預設MTU=1500 Byte,這是乙太網介面對IP層的約束,如果IP層有<=1500 byte 需要傳送,只需要一個IP包就可以完成傳送任務;如果IP層有> 1500 byte 資料需要傳送,需要分片才能完成傳送,這些分片有一個共同點,即IP Header ID相同。
MSS:Maximum Segment Size ,TCP提交給IP層最大分段大小,不包含TCP Header和 TCP Option,只包含TCP Payload ,MSS是TCP用來限制application層最大的傳送位元組數。如果底層物理介面MTU= 1500 byte,則 MSS = 1500- 20(IP Header) -20 (TCP Header) = 1460 byte,如果application 有2000 byte傳送,需要兩個segment才可以完成傳送,第一個TCP segment = 1460,第二個TCP segment = 540。
這是因為資料鏈路層--》IP層--》傳輸層--》應用層
#### 傳輸層
tcpdump -i any -nn -G 1800 -s 128 -Z root port 3307 and host 172.17.32.43 -w %Y_%m%d_%H%M_%S.cap
-G 30 間隔30秒生成檔案
-Z 需要建立新檔案時啟用
-s 限定資料大小,在忽略網路資料內容時使用。
-X 顯示包的內容
例子:
1、不要抓廣播 !broadcast
2、過濾MAC地址, ether host 00:80:**** 如果IP與埠不停變動,則啟用MAC抓包
3、IP地址過濾: host 192.168.0.120 可以考慮方向
4、埠:!port 80 不抓80
5、協議: arp、icmp
wireshark
在win10平臺,可以與程式碼聯調,但是要啟動長連結
高階功能:
1、資料流追蹤
作用:將TCP、UDP、SSL等資料流進行重組並完整呈現出來
操作方法:Analyze->Follow TCP stream 跟蹤tcp流
2、專家資訊說明
作用:對資料包中的特定狀態進行警告說明,包括錯誤、警告、注意、對話
操作方法:Analyze->Expert Info
3、統計摘要
作用:對抓取的資料包進行全域性統計
路徑:Statistics->Summary
應用層
谷歌http分析,前端效能分析:Chrome->開發者工具->Perfomance頁籤 ->Record -> 網頁操作->Stop
curl 測量網路延時,計算出各種階段的花費時間
Fiddler分析請求
==總結== 資料鏈路層: ifconfig , ethtool 檢視傳輸速度speed和傳輸介質
IP層: arp -n, route -n ,ping , tracert tracepath 判斷能否直達,關注TTL和跟蹤路徑
傳輸層: TCP握手和抓包,tcpdump
nginx引數配置 非常重要,記錄upstream_connect_time建立連線時間,upstream_response_time服務端處理時間 後面一個引數即服務端處理業務的時間
nginx 如果不開啟HTTP2.0,那麼也要求開啟長連線 keepalive 1000
ping命令關注TTL,時間 考慮訪問網站的時間和跳轉次數,以及丟包率
程序狀態分析--記憶體
外在表現: 請求長時間停頓,請求超時,閘道器504錯誤
根本原因: Full GC; Stop the World時間超過ngnix讀取時間;OOM的發生;JVM沒有啟動,沒有指定xmx
尋蹤原因: 容器內檢視死去應用 docker inspect 關注OOMKilled 和 CpuShares引數;docker stats 監控執行中的容器負載,CPU可以超過100%; docker logs 檢視容器的日誌
Java檢視記憶體資源內建的工具,pmap等,尤其是GC日誌檢視
依據Java記憶體結構分配JVM允許記憶體空間,最好不設定jvm執行記憶體空間的最大值,這等於限制Java
程序hang住後,無法用jstack dump棧資訊,執行jstack -F後進程恢復服務,minorGC結束。
關注Java Head堆記憶體和 非堆記憶體
非堆記憶體有元資料區,Java棧,本地棧,常量池,執行緒空間,GC
top -p pid 檢視程序記憶體佔用量 , 有兩個記憶體量,虛擬記憶體和實體記憶體
實體記憶體即系統分配給程序使用的記憶體空間
VIRT 程序使用的虛擬記憶體總量,單位kb。VIRT=SWAP+RES
RES 程序使用的、未被換出的實體記憶體大小,單位kb。RES=CODE+DATA
容器內新增-XX:NativeMemoryTracking=detail 引數解析記憶體佔用
/jdk/bin/jcmd 1 VM.native_memory scale=MB 命令檢視虛擬機器各區記憶體佔用情況
Java GC分析
事後分析,線上分析工具https://gceasy.io/ 和 Gcviewer ,GC分析一般是看老年代和新生代的空間是否充足等,不充足補足足夠的記憶體空間
Gc日誌主要分析full GC情況
top –H –p 1 ; 一般pid<100為gc執行緒
如果GC執行緒無法使用jmap等命令,hang住了,可以使用 jstack –F –m
大堆(Xmx>=4G)
推薦使用G1:-XX:+UseG1GC -XX:G1ReservePercent=5
小堆(Xmx<4G)
推薦使用吞吐量優先方式,即預設的GC方式。
檢視所有引數的方法
$java -XX:+PrintFlagsFinal -version
OOM問題分析
OOM問題是非常常見的,主要原因也是那幾個原因,比較好解決
可以在容器內設定掛載目錄,做資料對映,容器啟動後執行 jdk/bin/jmap -dump:live,format=b,file=/容器內掛載路徑/fin_20200822.bin 當發生OOM問題,記錄到對映區
MAT工具
[參考連結]https://zhuanlan.zhihu.com/p/56110317
dump檔案分析
JProfiler類似mat在IDEA執行的工具
程序行為分析--執行緒
識別執行緒
Monitor、Jstack、Arthas
使用後面兩個
1、堆疊的區域性資訊:呼叫層級關係、鎖與等待
2、一次堆疊的全域性資訊:鎖爭用、大多數執行緒在幹什麼、執行緒總數
3、多個堆疊的前後對比:方法是否長期執行、長期等待某個資源
監控執行緒,用上述的三個工具
阿里工具使用介紹
官方下載地址
火焰圖,Jprofiler工具檢視
Arthas的使用
在win系統使用Java自帶的VisualVM工具,檢視Java程序ID
下載好Arthas,解壓壓縮檔案,
as.bat pid
IDEA開發工具,搭配外掛使用,下載地址,這個外掛只能幫你製作命令,你需要到選中方法然後點選工具欄,製作命令
命令講解
-
dashboard 檢視程序的簡要報道
-
thread 1 將列印 ID 為 1 的執行緒的堆疊,通常是主函式執行緒。 $thread 1 | grep 'main('
-
jad demo.classname 命令反編譯類
-
sc -d *classname 可以列印JVM中載入的類詳細資訊
-
vmtool 可以獲取JVM中存活的某個類的例項或者強制GC
vmtool --action getInstances --className java.lang.String --limit 10
vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
vmtool --action forceGc -
monitor 監控方法的執行情況
monitor -c 5 demo.MathGame primeFactors ,-c 表示方法執行幾次為一個週期,一個週期統計一次情況 -
watch 可以觀察方法的指定呼叫情況,可以檢視方法的 返回值,異常,方法引數 ,預設的觀察維度是{params, target, returnObj} 引數,執行類,返回值,可以觀察方法執行前,執行後,異常發生前,異常發生後的資訊,預設在方法執行後
[x:] 指定輸出結果的屬性遍歷深度,預設為 1 ,如果引數是陣列,x=2 能觀察到數組裡面的內容
watch demo.MathGame primeFactors -x 2
watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2 觀察第一個引數和丟擲異常
watch demo.MathGame primeFactors 'target' 觀察物件的屬性
- trace 方法內部呼叫路徑,並輸出方法路徑上的每個節點上耗時,
trace demo.MathGame run -n 1 n為捕獲結果次數
trace --skipJDKMethod false demo.MathGame run 方法呼叫棧預設是不帶JDK的方法,但是加上相關引數解除限制
- stack 輸出當前方法被呼叫的呼叫路徑,很多時候我們都知道一個方法被執行,但這個方法被執行的路徑非常多,或者你根本就不知道這個方法是從那裡被執行了,此時你需要的是 stack 命令。
stack demo.MathGame primeFactors
- tt 方法執行資料的時空隧道,記錄下指定方法每次呼叫的入參和返回資訊,並能對這些不同的時間下呼叫進行觀測,主要觀察方法是否返回異常或者正常返回
tt -t demo.MathGame primeFactors -t 這個引數的表明希望記錄下類 *Test 的 print 方法的每次執行情況
IS-RET | 方法是否以正常返回的形式結束 |
---|---|
IS-EXP | 方法是否以拋異常的形式結束 |
解決方法過載
tt -t *Test print params.length==1
tt -t *Test print 'params[1] instanceof Integer'
tt -t *Test print params[0].mobile =="13989838402"
tt 命令由於儲存了當時呼叫的所有現場資訊,所以我們可以自己主動對一個 INDEX 編號的時間片自主發起一次呼叫
tt -l 展示全部的資料
tt -s 'method.name=="primeFactors"'
tt -i 1003 檢視具體的一欄
tt -i 1004 -p 發起一次呼叫,因為保留了資訊,但是是arthas發起的呼叫
- profiler 生成應用熱點的火焰圖,格式profiler action [actionArg]
actionArg屬性名模式 [i:]取樣間隔(單位:ns)(預設值:10'000'000,即10 ms) [f:]將輸出轉儲到指定路徑 [d:]執行評測指定秒 [e:]要跟蹤哪個事件(cpu, alloc, lock, cache-misses等),預設是cpu
一次取樣生產火焰圖 profiler start,profiler getSamples,profiler status, profiler stop,profiler stop --file /tmp/output.svg, profiler stop --format html,profiler resume 預設是CPU事件
trace demo.MathGame run '#cost > 10' watch/stack/trace這個三個命令都支援#cost,關注時間過長的或者過短的
通用的選項 -n 執行次數 '#cost > 10' 執行時間 '{params,target,returnObj,throwExp' 常用的觀察維度 'params[1].toLowerCase().contains("from t_sec_user ")' -n 1 更加複雜的引數