定位任意時刻性能問題,持續性能分析實踐解析

作者:義泊

01 持續性能剖析簡介

更好的應用性能,可以提供更好的用戶體驗,可以降低企業IT成本,可以讓系統更穩定和可靠。在應用性能剖析技術出現以前,開發人員排查問題只能依賴各種日誌和監控,這需要提前在應用代碼中埋點,不但對應用代碼侵入性較大且可能由於埋點不全而無法提供足夠信息,診斷問題非常費時,很多時候無法找出原因。

隨着應用性能剖析技術出現,開發人員可以很方便的找出應用程序性能瓶頸(如CPU利用率高、內存佔用高等),從而進行優化。但由於早期應用性能剖析技術開銷較大,只能在開發環境而不能在生產長時間開啓,生產環境出問題時很可能沒有被記錄下來,開發人員在開發環境模擬和復現問題很困難,導致解決問題的效率很低,也很有可能無法解決。

近些年來,性能剖析技術持續發展,功能越來越豐富,開銷也顯著改善,達到生產環境持續開啓水準,不過離廣泛普及還存在諸多障礙。性能剖析一般過程有三步:生產環境抓取、保存性能剖析文件、性能剖析文件可視化。當應用體量較大時,這3個步驟每步都存在着難度,需要解決大量計算、存儲、產品設計等多方面問題。

**ARMS Continuous Profiler [ 1] 應運而生,由阿里雲ARMS(應用實時監控服務 [ 2] )團隊和Dragonwell [ 3] **團隊聯合研發。它基於當前最成熟的性能剖析技術,將整個性能剖析過程產品化,適合在生成環境持續開啓。與常規性能剖析相比,ARMS Continuous Profiler增加時間維度,核心功能如下:

  • 定位任意時刻的性能問題(比如CPU佔用高、內存佔用高)
  • 支持兩個時段的性能對比,找出應用演進過程中的性能差異
  • 觀測應用的調用棧,以便更好的審視和理解代碼設計

02 ARMS 持續性能分析功能演示

我們舉例來說明如何用ARMS持續性能分析來解決問題。

常見場景一:CPU 熱點解析

  • 問題現象

以某圖書館的服務應用舉例,其Java進程佔用大量CPU,接口響應時間達到了十多秒,應用性能很差。

image.png

image.png

image.png

  • 找出熱點方法

因爲當前應用CPU佔用很高,因此我們直接在性能分析類型中選擇CPU Time菜單路徑:ARMS控制檯 -> 應用首頁 -> 應用診斷 -> CPU&內存診斷

image.png

image.png

從火焰圖我們可以看到,java.util.LinedList.node(int)方法佔用了85%的CPU,對應的業務代碼方法是DemoController.countAllBookPages(List),結合代碼,可以發現,這個方法對於對象很多的集合性能很差,因爲要從頭或者從尾部逐個遍歷。image.png

  • 修復問題

定位到原因後,我們可以通過兩個解決方案進行修復。第一個方法是將LinkedList修改爲下標訪問方式更高效的ArrayList

image.png

第二個方法是將LinkedList的遍歷算法從普通for循環修改爲增強的for循環

image.png

  • 性能驗證

將修復後的代碼重新部署,以相同壓力分別壓測兩種方案,可以看到接口響應時間顯著下降,Java進程CPU利用率顯著下降。

image.png

image.png

常見場景二:內存申請熱點

  • 問題現象

以某圖書館的服務應用舉例,其Java進程佔用大量CPU,接口響應時間達到十多秒,應用性能很差。

image.png

image.png

image.png

  • 找出熱點方法

因爲當前應用CPU佔用很高,我們直接在性能分析類型中選擇:CPU Time菜單路徑:ARMS控制檯 -> 應用首頁 -> 應用診斷 -> CPU&內存診斷

image.png

image.png

從CPU熱點方法,我們發現Java進程89%的時間都在做GC,說明應用存在很大的內存壓力。我們下一步選擇內存熱點剖析。

image.png

image.png

從上圖的內存申請熱點火焰圖,我們可以找到過去一段時間所有內存申請中,DemoController.queryAllBooks方法佔了99%,進一步檢查,可以發現業務代碼創建了2萬個大對象並保存到了List。

注:這個方法本來應該從數據庫中讀取2萬本書,這裏進行了簡化,但效果相同,都是在堆中創建了一個佔用大量內存的List

image.png

  • 修復問題

這個接口本來想實現的是按分頁查詢書籍列表,但由於實現錯誤,誤將所有書籍都查出來了然後最終只返回了指定分頁的部分,所以可以直接從數據庫中用分頁的方式查詢,這樣就可以避免大量的Java內存佔用。

image.png

  • 性能驗證

將修復後的代碼重新部署,以前相同壓力進行壓測,可以看到接口響應時間顯著下降,Java進程CPU利用率顯著下降。

image.png03

image.png

03 ARMS 持續性能分析的設計和實現

1、產品設計

產品整體分爲3個部分,第一個部分負責在應用端收集性能剖析數據,第二個部分用於傳輸和存儲剖析結果文件,第三部分用於查詢和展示。

image.png

第一個部分主要使用**Java Flight Recorder [ 4] async-profiler [ 5] **,我們會根據Java版本情況自動選擇其一,其核心功能是週期性對應用程序進行採樣,並且不會因爲安全點問題導致結果不準確。下圖是對一個線程採樣6次的例子,可以看到每次採樣瞬間的調用棧。最終保存爲JFR格式的文件。

image.png

第二個部分比較重要的是JFR Analyzer,其核心功能是讀取JFR文件,對其進行解析、計算和聚合,最終生成便於查詢和展示的中間結果。第三個部分的核心功能是將剖析結果展示爲表格或火焰圖,也要支持對比能力。

2、Java Flight Recorder

JFR是OpenJDK內置的低開銷監控和性能剖析工具,深度集成在虛擬機各個角落。當Oracle在OpenJDK11上開源JDK Flight Recorder之後,阿里巴巴也是作爲主要貢獻者,與RedHat等社區貢獻者一起將 JFR 移植到OpenJDK 8。

JFR由兩個部分組成: 第1個部分分佈在虛擬機各個關鍵路徑上,負責捕獲信息。

第2個部分是虛擬機內單獨模塊,負責接收和存儲第1個部分產生的數據,這些數據通常也叫做事件。

JFR包含160種以上事件,JFR事件包含很多有用的上下文信息及時間戳。比如方法執行調用棧、文件訪問、特定GC階段的發生,或特定GC階段、耗時。

image.png

image.png

3、async-profiler

async-profiler是一個低開銷的Java性能剖析工具,依靠JVM的特定API進行CPU和內存申請的剖析。

因爲OracleJDK 8上JFR功能是商業特性,所以在OracleJDK8上我們用async-profiler作爲替換技術,實現相同剖析能力。而對於OpenJDK8,由於內存申請熱點剖析功能存在較大性能開銷,我們也用async-profiler作爲替代技術。

async-profiler使用C++開發,以動態庫方式加載到JVM進程中,支持生成JFR格式文件,這樣不論我們用JFR還是async-profiler,因爲文件格式相同,所以分析和存儲方案都可以複用。

image.png

4、JFR File Analyzer

JFR File Analyzer的輸入是JFR文件,輸出是一種支持按時間範圍高效查詢的樹狀結構。一個JFR文件中可以包含CPU熱點、內存申請熱點等多個方面的數據,每個方面都有對應的解析和存儲實現。

image.png04

04 總結

本文介紹了持續性能剖析的產生背景,通過兩個例子演示了ARMS Continuous Profiler的實際使用場景,也對ARMS Continuous Profiler的設計和核心模塊進行了介紹,其主要特點如下:

image.png

image.png

對ARMS Continuous Profiler感興趣的讀者,可以加入專屬服務釘羣,或者閱讀產品文檔,歡迎試用和交流。

👉 專屬服務釘羣:22560019672

image.png

📒 文檔:https://help.aliyun.com/document_detail/473143.html05

05 相關鏈接

[1] ARMS Continuous Profiler

https://help.aliyun.com/document_detail/473143.html

[2] 應用實時監控服務

https://help.aliyun.com/product/34364.html

[3] Dragonwell

https://dragonwell-jdk.io/#/index

[4] Java Flight Recorder

https://docs.oracle.com/javacomponents/jmc-5-4/jfr-runtime-guide/about.htm

[5] async-profiler

https://github.com/async-profiler/async-profiler

image.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章