性能診斷利器 JProfiler 快速入門和最佳實踐

背景

性能診斷是軟件工程師在平常工做中須要常常面對和解決的問題,在用戶體驗至上的今天,解決好應用的性能問題能帶來很是大的收益。Java 做爲最流行的編程語言之一,其應用性能診斷一直受到業界普遍關注。可能形成 Java 應用出現性能問題的因素很是多,例如線程控制、磁盤讀寫、數據庫訪問、網絡I/O、垃圾收集等。想要了定位這些問題,一款優秀的性能診斷工具必不可少。本文將介紹 Java 性能診斷過程當中的經常使用工具,並重點介紹其中的優秀表明 JProfiler 的基本原理和最佳實踐(本文所作的調研基於jprofiler10.1.4)。html

Java 性能診斷工具簡介

在 Java 的世界裏,有許多診斷工具可供選擇,既包括像 jmap、jstat 這樣的簡單命令行工具,又包括 JVisualvm、JProfiler 等圖形化綜合診斷工具,同時還有 SkyWalking、ARMS 這樣的針對分佈式應用的性能監控系統。下面分別對其進行介紹。java

簡單命令行工具

JDK 內置了許多命令行工具,它們可用來獲取目標 JVM 不一樣方面、不一樣層次的信息。git

  • jinfo - 用於實時查看和調整目標 JVM 的各項參數。
  • jstack - 用於獲取目標 Java 進程內的線程堆棧信息,可用來檢測死鎖、定位死循環等。
  • jmap - 用於獲取目標 Java 進程的內存相關信息,包括 Java 堆各區域的使用狀況、堆中對象的統計信息、類加載信息等。
  • jstat - 一款輕量級多功能監控工具,可用於獲取目標 Java 進程的類加載、JIT 編譯、垃圾收集、內存使用等信息。
  • jcmd - 相比 jstat 功能更爲全面的工具,可用於獲取目標 Java 進程的性能統計、JFR、內存使用、垃圾收集、線程堆棧、JVM 運行時間等信息。

圖形化綜合診斷工具

使用上述命令行工具或組合能幫您獲取目標 Java 應用性能相關的基礎信息,但它們存在下列侷限:github

  1. 沒法獲取方法級別的分析數據,如方法間的調用關係、各方法的調用次數和調用時間等(這對定位應用性能瓶頸相當重要)。
  2. 要求用戶登陸到目標 Java 應用所在的宿主機上,使用起來不是很方便。
  3. 分析數據經過終端輸出,結果展現不夠直觀。

下面介紹幾款圖形化的綜合性能診斷工具。數據庫

JVisualvmapache

JVisualvm 是 JDK 內置的可視化性能診斷工具,它經過 JMX、jstatd、Attach API 等方式獲取目標 JVM 的分析數據,包括 CPU 使用率、內存使用量、線程堆棧信息等。此外,它還能直觀地展現 Java 堆中各對象的數量和大小、各 Java 方法的調用次數和執行時間等。編程

JProfiler緩存

JProfiler 是由 ej-technologies 公司開發的一款 Java 應用性能診斷工具。它聚焦於四個重要主題上。性能優化

  1. 方法調用 - 對方法調用的分析能夠幫助您瞭解應用程序正在作什麼,並找到提升其性能的方法。
  2. 內存分配 - 經過分析堆上對象、引用鏈和垃圾收集能幫您修復內存泄漏問題,優化內存使用。
  3. 線程和鎖 - JProfiler 提供多種針對線程和鎖的分析視圖助您發現多線程問題。
  4. 高級子系統 - 許多性能問題都發生在更高的語義級別上。例如,對於JDBC調用,您可能但願找出執行最慢的 SQL 語句。JProfiler 支持對這些子系統進行集成分析。

分佈式應用性能診斷

若是隻須要診斷單機 Java 應用的性能瓶頸,上面介紹的診斷工具就已經夠用了。但隨着現代系統架構逐漸從單體轉變爲分佈式、微服務,單純使用上述工具每每沒法知足需求,這時就須要藉助 JaegerARMSSkyWalking 這些分佈式追蹤系統提供的全鏈路追蹤功能。分佈式追蹤系統種類繁多,但實現原理都大同小異,它們經過代碼埋點的方式記錄 tracing 信息,經過 SDK 或 agent 將記錄的數據傳輸至中央處理系統,最後提供 query 接口對結果進行展現和分析,想了解更多分佈式追蹤系統的原理可參考文章開放分佈式追蹤(OpenTracing)入門與 Jaeger 實現網絡

JProfiler 簡介

核心組件

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 提供了一系列命令行工具以實現不一樣的功能。

  • jpcontroller - 用於控制 agent 的採集行爲。它經過 agent 註冊的 JProfiler MBean 向 agent 傳遞命令。
  • jpenable - 用於將 agent 加載到一個正在運行的 JVM 上。
  • jpdump - 用於獲取正在運行的 JVM 的堆快照。
  • jpexport & jpcompare - 用於從保存的快照中提取數據並建立 HTML 報告。

安裝配置

JProfiler 同時支持診斷本地和遠程 Java 應用的性能。若是您須要實時採集並展現遠程 JVM 的分析數據,須要完成以步驟:

  1. 在本地安裝 JProfiler UI。
  2. 在遠程宿主機上安裝 JProfiler agent 並讓其被目標 JVM 加載。
  3. 配置 UI 到 agent 的鏈接。

具體步驟可參考文檔 Installing JProfiler 和 Profiling A JVM

最佳實踐

本章將以高性能寫 LogHub 類庫 Aliyun LOG Java Producer 爲原型,帶您瞭解如何使用 JProfiler 剖析它的性能。若是您的應用或者您在使用 producer 的過程當中遇到了性能問題,也能夠用相似的方式定位問題根因。若是您還不瞭解 producer 的功能,建議先閱讀文章日誌上雲利器 - Aliyun LOG Java Producer。本章使用的樣例代碼參見 SamplePerformance.java

JProfiler 設置

數據採集模式

JProfier 提供兩種數據採集模式 Sampling 和 Instrumentation。

  • Sampling - 適合於不要求數據徹底精確的場景。優勢是對系統性能的影響較小,缺點是某些特性不支持(如方法級別的統計信息)。
  • Instrumentation - 完整功能模式,統計信息也是精確的。缺點是若是須要分析的類比較多,對應用性能影響較大。爲了下降影響,每每須要和 Filter 一塊兒使用。

因爲咱們須要獲取方法級別的統計信息,這裏選擇了 Instrumentation 模式。同時配置了 Filter,讓 agent 只記錄位於 Java 包com.aliyun.openservices.aliyun.log.producer下的類和類com.aliyun.openservices.log.Client的 CPU 分析數據。

應用啓動模式

經過爲 JProfiler agent 指定不一樣的參數能夠控制應用的啓動模式。

  • 等待模式 - 只有在 Jprofiler GUI 和 agent 創建鏈接並完成分析配置設置後,應用纔會真正啓動。在這種模式下,您可以獲取應用啓動時期的分析數據。對應的命令爲-agentpath:<path to native library>=port=8849
  • 當即啓動模式 - 應用會當即啓動,Jprofiler GUI 會在須要時和 agent 創建鏈接並設置分析配置。這種模式相對靈活,但會丟失應用啓動初期的分析數據。對應的命令爲-agentpath:<path to native library>=port=8849,nowait
  • 離線模式 - 經過觸發器記錄數據、保存快照供過後分析。對應的命令爲-agentpath:<path to native library>=offline,id=xxx,config=/config.xml

由於是在測試環境,同時對應用啓動初期的性能也比較關注,這裏選擇了默認的等待模式。

使用 JProfiler 診斷性能

在完成 JProfiler 的設置後,即可以對 Producer 的性能進行診斷。

Overview

在概覽頁咱們能夠清晰的看到內存使用量、垃圾收集活動、類加載數量、線程個數和狀態、CPU 使用率等指標隨時間變化的趨勢。

經過此圖,咱們能夠做出以下基本判斷:

  1. 程序在運行過程當中會產生大量對象,但這些對象生命週期極短,大部分都能被垃圾收集器及時回收,不會形成內存無限增加。
  2. 加載類的數量在程序初始時增加較快,隨後保持平穩,符合預期。
  3. 在程序運行過程當中,有大量線程處於阻塞狀態,須要重點關注。
  4. 在程序剛啓動時,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 的診斷可知應用不存在大的性能問題,也不存在內存泄漏。下一步的優化方向是提高對象的序列化效率。

參考資料



本文做者:吳波bruce_wu

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索