性能診斷是軟件工程師在平常工做中須要常常面對和解決的問題,在用戶體驗至上的今天,解決好應用的性能問題能帶來很是大的收益。Java 做爲最流行的編程語言之一,其應用性能診斷一直受到業界普遍關注。可能形成 Java 應用出現性能問題的因素很是多,例如線程控制、磁盤讀寫、數據庫訪問、網絡I/O、垃圾收集等。想要了定位這些問題,一款優秀的性能診斷工具必不可少。本文將介紹 Java 性能診斷過程當中的經常使用工具,並重點介紹其中的優秀表明 JProfiler 的基本原理和最佳實踐(本文所作的調研基於jprofiler10.1.4
)。html
在 Java 的世界裏,有許多診斷工具可供選擇,既包括像 jmap、jstat 這樣的簡單命令行工具,又包括 JVisualvm、JProfiler 等圖形化綜合診斷工具,同時還有 SkyWalking、ARMS 這樣的針對分佈式應用的性能監控系統。下面分別對其進行介紹。java
JDK 內置了許多命令行工具,它們可用來獲取目標 JVM 不一樣方面、不一樣層次的信息。git
使用上述命令行工具或組合能幫您獲取目標 Java 應用性能相關的基礎信息,但它們存在下列侷限:github
下面介紹幾款圖形化的綜合性能診斷工具。數據庫
JVisualvmapache
JVisualvm 是 JDK 內置的可視化性能診斷工具,它經過 JMX、jstatd、Attach API 等方式獲取目標 JVM 的分析數據,包括 CPU 使用率、內存使用量、線程堆棧信息等。此外,它還能直觀地展現 Java 堆中各對象的數量和大小、各 Java 方法的調用次數和執行時間等。編程
JProfiler緩存
JProfiler 是由 ej-technologies 公司開發的一款 Java 應用性能診斷工具。它聚焦於四個重要主題上。性能優化
若是隻須要診斷單機 Java 應用的性能瓶頸,上面介紹的診斷工具就已經夠用了。但隨着現代系統架構逐漸從單體轉變爲分佈式、微服務,單純使用上述工具每每沒法知足需求,這時就須要藉助 Jaeger、ARMS、SkyWalking 這些分佈式追蹤系統提供的全鏈路追蹤功能。分佈式追蹤系統種類繁多,但實現原理都大同小異,它們經過代碼埋點的方式記錄 tracing 信息,經過 SDK 或 agent 將記錄的數據傳輸至中央處理系統,最後提供 query 接口對結果進行展現和分析,想了解更多分佈式追蹤系統的原理可參考文章開放分佈式追蹤(OpenTracing)入門與 Jaeger 實現。網絡
JProfiler 包含用於採集目標 JVM 分析數據的 JProfiler agent、用於可視化分析數據的 JProfiler UI、提供各類功能的命令行工具,它們之間的關係以下圖所示。
JProfiler agent
JProfiler agent 是一個本地庫,它能夠在 JVM 啓動時經過參數-agentpath:<path to native library>
進行加載或者在程序運行時經過 JVM Attach 機制進行加載。Agent 被成功加載後,會設置 JVMTI 環境,監聽虛擬機產生的事件,如類加載、線程建立等。例如,當它監聽到類加載事件後,會給這些類注入用於執行度量操做的字節碼。
JProfiler UI
JProfiler UI 是一個可獨立部署的組件,它經過 socket 和 agent 創建鏈接。這意味着不論目標 JVM 運行在本地仍是遠端,JProfiler UI 和 agent 間的通訊機制都是同樣的。
JProfiler UI 的主要功能是展現經過 agent 採集上來的分析數據,此外還能夠經過它控制 agent 的採集行爲,將快照保存至磁盤,展現保存的快照。
命令行工具
JProfiler 提供了一系列命令行工具以實現不一樣的功能。
JProfiler 同時支持診斷本地和遠程 Java 應用的性能。若是您須要實時採集並展現遠程 JVM 的分析數據,須要完成以步驟:
具體步驟可參考文檔 Installing JProfiler 和 Profiling A JVM。
本章將以高性能寫 LogHub 類庫 Aliyun LOG Java Producer 爲原型,帶您瞭解如何使用 JProfiler 剖析它的性能。若是您的應用或者您在使用 producer 的過程當中遇到了性能問題,也能夠用相似的方式定位問題根因。若是您還不瞭解 producer 的功能,建議先閱讀文章日誌上雲利器 - Aliyun LOG Java Producer。本章使用的樣例代碼參見 SamplePerformance.java。
數據採集模式
JProfier 提供兩種數據採集模式 Sampling 和 Instrumentation。
因爲咱們須要獲取方法級別的統計信息,這裏選擇了 Instrumentation 模式。同時配置了 Filter,讓 agent 只記錄位於 Java 包com.aliyun.openservices.aliyun.log.producer
下的類和類com.aliyun.openservices.log.Client
的 CPU 分析數據。
應用啓動模式
經過爲 JProfiler agent 指定不一樣的參數能夠控制應用的啓動模式。
-agentpath:<path to native library>=port=8849
。-agentpath:<path to native library>=port=8849,nowait
。-agentpath:<path to native library>=offline,id=xxx,config=/config.xml
。由於是在測試環境,同時對應用啓動初期的性能也比較關注,這裏選擇了默認的等待模式。
在完成 JProfiler 的設置後,即可以對 Producer 的性能進行診斷。
Overview
在概覽頁咱們能夠清晰的看到內存使用量、垃圾收集活動、類加載數量、線程個數和狀態、CPU 使用率等指標隨時間變化的趨勢。
經過此圖,咱們能夠做出以下基本判斷:
CPU views
CPU views 下的各個子視圖展現了應用中各方法的執行次數、執行時間、調用關係等信息,能幫咱們定位對應用性能影響最大的方法。
Call Tree
Call tree 經過樹形圖清晰地展示了方法間的層次調用關係。同時,JProfiler 將子方法按照它們的執行總時間由大到小排序,這能讓您快速定位關鍵方法。
對於 Producer 而言,方法SendProducerBatchTask.run()
耗時最多,繼續向下查看會發現該方法的主要時間消耗在了執行方法Client.PutLogs()
上。
Hot Spots
若是您的應用方法不少,且不少子方法的執行時間比較接近,使用 hot spots 視圖每每能助您更快地定位問題。該視圖能根據方法的單獨執行時間、總執行時間、平均執行時間、調用次數等屬性對它們排序。其中,單獨執行時間等於該方法的總執行時間減去全部子方法的總執行時間。
在該視圖下,能夠看到Client.PutLogs()
,LogGroup.toByteArray()
,SamplePerformance$1.run()
是單獨執行時間耗時最多的三個方法。
Call Graph
找到了關鍵方法後,call graph 視圖能爲您呈現與該方法直接關聯的全部方法。這有助於咱們對症下藥,制定合適的性能優化策略。
這裏,咱們觀察到方法Client.PutLogs()
執行的主要時間花費在了對象序列化上,所以性能優化的關鍵是提供執行效率更高的序列化方法。
Live memory
Live memory 下的各個子視圖能讓您掌握內存的具體分配和使用狀況,助您判斷是否存在內存泄漏問題。
All Objects
All Objects 視圖展現了當前堆中各類對象的數量和總大小。由圖可知,程序在運行過程當中構造出了大量 LogContent 對象。
Allocation Call Tree
Allocation Call Tree 以樹形圖的形式展現了各方法分配的內存大小。能夠看到,SamplePerformance$1.run()
和SendProducerBatchTask.run()
是內存分配大戶。
Allocation Hot Spots
若是方法比較多,您還能夠經過 Allocation Hot Spots 視圖快速找出分配對象最多的方法。
Thread History
線程歷史記錄視圖直觀地展現了各線程在不一樣時間點的狀態。
不一樣線程執行的任務不一樣,所展示的狀態特徵也不一樣。
pool-1-thread-<M>
會循環調用producer.send()
方法異步發送數據,它們在程序剛啓動時一直處於運行狀態,但隨後在大部分時間裏處於阻塞狀態。這是由於 producer 發送數據的速率低於數據的產生速率,且單個 producer 實例能緩存的數據大小有限。在程序運行初始,producer 有足夠空間緩存待發送數據,因此pool-1-thread-<M>
一直處於運行狀態,這也就解釋了爲什麼程序在剛啓動時 CPU 使用率較高。隨着時間的推移,producer 的緩存被逐漸耗盡,pool-1-thread-<M>
必須等到 producer 「釋放」出足夠的空間纔有機會繼續運行,這也是爲何咱們會觀察到大量線程處於阻塞狀態。aliyun-log-producer-0-mover
負責將超時 batch 投遞到發送線程池中。因爲發送速率較快,batch 會因緩存的數據達到了上限被pool-1-thread-<M>
直接投遞到發送線程池中,所以 mover 線程在大部分時間裏都處於等待狀態。aliyun-log-producer-0-io-thread-<N>
做爲真正執行數據發送任務的線程有一部分時間花在了網絡 I/O 狀態。aliyun-log-producer-0-success-batch-handler
用於處理髮送成功的 batch。因爲回調函數比較簡單,執行時間短,它在大部分時間裏都處於等待狀態。aliyun-log-producer-0-failure-batch-handler
用於處理髮送失敗的 batch。因爲沒有數據發送失敗,它一直處於等待狀態。經過上述分析可知,這些線程的狀態特徵都是符合預期的。
Overhead Hot Spots Detected
當程序運行結束後,JProfiler 會彈出一個對話框展現那些頻繁被調用,但執行時間又很短的方法。在下次診斷時,您可讓 JProfiler agent 在分析過程當中忽略掉這些方法以減輕對應用性能的影響。
經過 JProfiler 的診斷可知應用不存在大的性能問題,也不存在內存泄漏。下一步的優化方向是提高對象的序列化效率。
本文爲雲棲社區原創內容,未經容許不得轉載。