如果你經常遇到 Java 線上性能問題束手無策,看着線上服務 CPU 飆升一籌莫展,發現內存不斷泄露滿臉茫然。別慌,這里有一款低開銷、自帶火焰圖、讓你大呼好用的 Java 性能分析工具 - async-profiler。
最近 Arthas 性能分析工具上線了火焰圖分析功能,Arthas 使用 async-profiler 生成 CPU/內存火焰圖進行性能分析,彌補了之前內存分析的不足。在 Arthas 上使用還是比較方便的,使用方式可以看官方文檔。這篇文章介紹 async-profiler 相關內容。
Arthas 火焰圖官方文檔:https://arthas.aliyun.com/doc/profiler.html
async-profiler 介紹
async-profiler 是一款開源的 Java 性能分析工具,原理是基於 HotSpot 的 API,以微乎其微的性能開銷收集程序運行中的堆棧信息、內存分配等信息進行分析。
使用 async-profiler 可以做下面幾個方面的分析。
- CPU cycles
- Hardware and Software performance counters like cache misses, branch misses, page faults, context switches etc.
- Allocations in Java Heap
- Contented lock attempts, including both Java object monitors and ReentrantLocks
我們常用的是 CPU 性能分析和 Heap 內存分配分析。在進行 CPU 性能分析時,僅需要非常低的性能開銷就可以進行分析,這也是這個工具的優點之一。
在進行 Heap 分配分析時,async-profiler 工具會收集內存分配信息,而不是去檢測占用 CPU 的代碼。async-profiler 不使用侵入性的技術,例如字節碼檢測工具或者探針檢測等,這也說明 async-profiler 的內存分配分析像 CPU 性能分析一樣,不會產生太大的性能開銷,同時也不用寫出龐大的堆棧文件再去進行進一步處理,。
async-profile 目前支持 Linux 和 macOS 平台(macOS 下只能分析用戶空間的代碼)。
- Linux / x64 / x86 / ARM / AArch64
- macOS / x64
async-profiler 工具在采樣后可以生成采樣結果的日志報告,也可以生成 SVG 格式的火焰圖,在之前生成火焰圖要使用 FlameGraph 工具。現在已經不需要了,從 1.2 版本開始,就已經內置了開箱即用的 SVG 文件生成功能。
其他信息可以看官方文檔:https://github.com/jvm-profiling-tools/async-profiler
async-profiler 安裝
# 從阿里雲下載jar包
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 運行
java -jar arthas-boot.jar
arthas-boot.jar中直接內置了profiler
async-profiler 使用
[arthas@69686]$ profiler execute 'start'
Started [cpu] profiling
[arthas@69686]$ profiler execute 'stop,file=/Users/admin/Desktop/result.svg'
OK
async-profiler 案例
上面說完了 async-profiler 工具的作用和使用方式,既然能進行 CPU 性能分析和 Heap 內存分配分析,那么我們就寫幾個不一般的方法分析試試看。看看是不是有像上面介紹的那么好用。
Java 案例編碼
很簡單的幾個方法,hotmethod 方法寫了幾個常見操作,三個方法中很明顯 hotmethod3 方法里的生成 UUID 和 replace(需要正則匹配)操作消耗的 CPU 性能會較多。allocate 方法里因為要不斷的創建長度為 6萬的數組,消耗的內存空間一定是最多的。
import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;
/**
* <p>
* 模擬熱點代碼
*
* @Author niujinpeng
*/
public class HotCode {
private static volatile int value;
private static Object array;
public static void main(String[] args) {
while (true) {
hotmethod1();
hotmethod2();
hotmethod3();
allocate();
}
}
/**
* 生成 6萬長度的數組
*/
private static void allocate() {
array = new int[6 * 1000];
array = new Integer[6 * 1000];
}
/**
* 生成一個UUID
*/
private static void hotmethod3() {
ArrayList<String> list = new ArrayList<>();
UUID uuid = UUID.randomUUID();
String str = uuid.toString().replace("-", "");
list.add(str);
}
/**
* 數字累加
*/
private static void hotmethod2() {
value++;
}
/**
* 生成一個隨機數
*/
private static void hotmethod1() {
Random random = new Random();
int anInt = random.nextInt();
}
}
CPU 性能分析
$ profiler start
Started [cpu] profiling
關於火焰圖怎么看,一言以蔽之:火焰圖里,橫條越長,代表使用的越多,從下到上是調用堆棧信息。在這個圖里可以看到 main 方法上面的調用中 hotmethod3 方法的 CPU 使用是最多的,點擊這個方法。還可能看到更詳細的信息。
可以看到 replace 方法占用的 CPU 最多,也是程序中性能問題所在,是需要注意的地方。
Heap 內存分析
還是上面運行的程序,這次分析內存使用情況。
profiler start --event alloc
得到的 svg 文件使用瀏覽器打開,可以看到內存分配情況。
依舊是橫條越長,代表使用的越多,從下到上是調用堆棧信息。從圖里可以看出來 main 方法調用的 allocate 方法使用的內存最多,這個方法里的 Integer 類型數組占用的內存又最多,為 71%。
文中測試代碼已經上傳到 Github:https://github.com/niumoo/lab-notes