超好用的自帶火焰圖的 Java 性能分析工具 Async-profiler 了解一下

火焰圖

如果你經(jīng)常遇到 Java 線上性能問題束手無策试读,看著線上服務(wù) CPU 飆升一籌莫展,發(fā)現(xiàn)內(nèi)存不斷泄露滿臉茫然。別慌瞻颂,這里有一款低開銷豺谈、自帶火焰圖、讓你大呼好用的 Java 性能分析工具 - async-profiler贡这。

最近 Arthas 性能分析工具上線了火焰圖分析功能茬末,Arthas 使用 async-profiler 生成 CPU/內(nèi)存火焰圖進(jìn)行性能分析,彌補(bǔ)了之前內(nèi)存分析的不足盖矫。在 Arthas 上使用還是比較方便的丽惭,使用方式可以看官方文檔。這篇文章介紹 async-profiler 相關(guān)內(nèi)容辈双。

Arthas 火焰圖官方文檔:https://alibaba.github.io/arthas/profiler.html

如果你想了解更多 Arthas 信息责掏,可以參考之前文章:Arthas - Java 線上問題定位處理的終極利器

async-profiler 介紹

async-profiler 是一款開源的 Java 性能分析工具,原理是基于 HotSpot 的 API湃望,以微乎其微的性能開銷收集程序運(yùn)行中的堆棧信息换衬、內(nèi)存分配等信息進(jìn)行分析。

使用 async-profiler 可以做下面幾個(gè)方面的分析喜爷。

  • 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 內(nèi)存分配分析冗疮。在進(jìn)行 CPU 性能分析時(shí)萄唇,僅需要非常低的性能開銷就可以進(jìn)行分析檩帐,這也是這個(gè)工具的優(yōu)點(diǎn)之一。

在進(jìn)行 Heap 分配分析時(shí)另萤,async-profiler 工具會(huì)收集內(nèi)存分配信息湃密,而不是去檢測占用 CPU 的代碼。async-profiler 不使用侵入性的技術(shù)四敞,例如字節(jié)碼檢測工具或者探針檢測等泛源,這也說明 async-profiler 的內(nèi)存分配分析像 CPU 性能分析一樣,不會(huì)產(chǎn)生太大的性能開銷忿危,同時(shí)也不用寫出龐大的堆棧文件再去進(jìn)行進(jìn)一步處理达箍,。

async-profile 目前支持 Linux 和 macOS 平臺(tái)(macOS 下只能分析用戶空間的代碼)铺厨。

  • Linux / x64 / x86 / ARM / AArch64
  • macOS / x64

async-profiler 工具在采樣后可以生成采樣結(jié)果的日志報(bào)告缎玫,也可以生成 SVG 格式的火焰圖,在之前生成火焰圖要使用 FlameGraph 工具〗庾遥現(xiàn)在已經(jīng)不需要了赃磨,從 1.2 版本開始,就已經(jīng)內(nèi)置了開箱即用的 SVG 文件生成功能洼裤。

其他信息可以看官方文檔:https://github.com/jvm-profiling-tools/async-profiler

async-profiler 安裝

下載 async-profiler 工具可以在官方的 Github 上直接下載編譯好的文件邻辉,如果你就是想體驗(yàn)手動(dòng)擋的感覺,也可以克隆項(xiàng)目,手動(dòng)編譯一下值骇,不得不說這個(gè)工具十分的易用莹菱,我在手動(dòng)編譯的過程十分順滑,沒有出現(xiàn)任何問題吱瘩。

如果你想下載編譯好的芒珠,可以到這里下載。

https://github.com/jvm-profiling-tools/async-profiler/releases

如果想體驗(yàn)手動(dòng)擋的感覺搅裙,可以克隆整個(gè)項(xiàng)目皱卓,進(jìn)項(xiàng)項(xiàng)目編譯。

手動(dòng)編譯的環(huán)境要求部逮。

  • JDK
  • GCC

下面是手動(dòng)安裝的操作命令娜汁。

git clone https://github.com/jvm-profiling-tools/async-profiler
cd async-profiler
make

執(zhí)行 make 命令編譯后會(huì)在項(xiàng)目的目錄下生成一個(gè) build 文件夾,里面存放著編譯的結(jié)果兄朋。下面是我手動(dòng)編譯的過程輸出掐禁。

?  develop git clone https://github.com/jvm-profiling-tools/async-profiler
Cloning into 'async-profiler'...
remote: Enumerating objects: 69, done.
remote: Counting objects: 100% (69/69), done.
remote: Compressing objects: 100% (54/54), done.
remote: Total 1805 (delta 34), reused 32 (delta 15), pack-reused 1736
Receiving objects: 100% (1805/1805), 590.78 KiB | 23.00 KiB/s, done.
Resolving deltas: 100% (1288/1288), done.
?  develop cd async-profiler
?  async-profiler git:(master) make
mkdir -p build
g++ -O2 -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -DPROFILER_VERSION=\"1.6\" -I/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/include/darwin -fPIC -shared -o build/libasyncProfiler.so src/*.cpp -ldl -lpthread
gcc -O2 -DJATTACH_VERSION=\"1.5\" -o build/jattach src/jattach/jattach.c
mkdir -p build/classes
/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/javac -source 6 -target 6 -d build/classes src/java/one/profiler/AsyncProfiler.java src/java/one/profiler/AsyncProfilerMXBean.java src/java/one/profiler/Counter.java src/java/one/profiler/Events.java
警告: [options] 未與 -source 1.6 一起設(shè)置引導(dǎo)類路徑
1 個(gè)警告
/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/jar cvf build/async-profiler.jar -C build/classes .
已添加清單
正在添加: one/(輸入 = 0) (輸出 = 0)(存儲(chǔ)了 0%)
正在添加: one/profiler/(輸入 = 0) (輸出 = 0)(存儲(chǔ)了 0%)
正在添加: one/profiler/AsyncProfiler.class(輸入 = 1885) (輸出 = 908)(壓縮了 51%)
正在添加: one/profiler/Events.class(輸入 = 405) (輸出 = 286)(壓縮了 29%)
正在添加: one/profiler/Counter.class(輸入 = 845) (輸出 = 473)(壓縮了 44%)
正在添加: one/profiler/AsyncProfilerMXBean.class(輸入 = 631) (輸出 = 344)(壓縮了 45%)
rm -rf build/classes
?  async-profiler git:(master)

async-profiler 使用

運(yùn)行項(xiàng)目里的 profiler.sh 可以看到 async-profiler 的使用幫助文檔。

?  async-profiler git:(master) ./profiler.sh
Usage: ./profiler.sh [action] [options] <pid>
Actions:
  start             start profiling and return immediately
  resume            resume profiling without resetting collected data
  stop              stop profiling
  status            print profiling status
  list              list profiling events supported by the target JVM
  collect           collect profile for the specified period of time
                    and then stop (default action)
Options:
  -e event          profiling event: cpu|alloc|lock|cache-misses etc.
  -d duration       run profiling for <duration> seconds
  -f filename       dump output to <filename>
  -i interval       sampling interval in nanoseconds
  -j jstackdepth    maximum Java stack depth
  -b bufsize        frame buffer size
  -t                profile different threads separately
  -s                simple class names instead of FQN
  -g                print method signatures
  -a                annotate Java method names
  -o fmt            output format: summary|traces|flat|collapsed|svg|tree|jfr
  -v, --version     display version string

  --title string    SVG title
  --width px        SVG width
  --height px       SVG frame height
  --minwidth px     skip frames smaller than px
  --reverse         generate stack-reversed FlameGraph / Call tree

  --all-kernel      only include kernel-mode events
  --all-user        only include user-mode events
  --sync-walk       use synchronous JVMTI stack walker (dangerous!)

<pid> is a numeric process ID of the target JVM
      or 'jps' keyword to find running JVM automatically

Example: ./profiler.sh -d 30 -f profile.svg 3456
         ./profiler.sh start -i 999000 jps
         ./profiler.sh stop -o summary,flat jps

可以看到使用的方式是:Usage: ./profiler.sh [action] [options] <pid>颅和,也就是 命令+操作+參數(shù)+PID傅事。

常用的使用的幾個(gè)步驟:

  1. 查看 java 進(jìn)程的 PID(可以使用 jps )。
  2. 使用 ./profiler.sh start <pid> 開始采樣峡扩。
  3. 使用 ./profiler.sh status <pid> 查看已經(jīng)采樣的時(shí)間蹭越。
  4. 使用 ./profiler.sh stop <pid> 停止采樣,輸出結(jié)果教届。

這種方式使用起來多費(fèi)勁啊响鹃,而且最后輸出的是文本結(jié)果,看起來更是費(fèi)勁案训,為了不那么費(fèi)勁买置,可以使用幫助里給的采樣后生成 SVG 文件例子。

./profiler.sh -d 30 -f profile.svg 3456

這個(gè)命令的意思是强霎,對(duì) PID 為 3456 的 java 進(jìn)程采樣 30 秒忿项,然后生成 profile.svg 結(jié)果文件。

默認(rèn)情況下是分析 CPU 性能城舞,如果要進(jìn)行其他分析轩触,可以使用 -e 參數(shù)。

-e event     profiling event: cpu|alloc|lock|cache-misses etc.

可以看到支持的分析事件有 CPU椿争、Alloc怕膛、Lock、Cache-misses 秦踪。

async-profiler 案例

上面說完了 async-profiler 工具的作用和使用方式褐捻,既然能進(jìn)行 CPU 性能分析和 Heap 內(nèi)存分配分析掸茅,那么我們就寫幾個(gè)不一般的方法分析試試看∧眩看看是不是有像上面介紹的那么好用昧狮。

Java 案例編碼

很簡單的幾個(gè)方法,hotmethod 方法寫了幾個(gè)常見操作板壮,三個(gè)方法中很明顯 hotmethod3 方法里的生成 UUID 和 replace(需要正則匹配)操作消耗的 CPU 性能會(huì)較多逗鸣。allocate 方法里因?yàn)橐粩嗟膭?chuàng)建長度為 6萬的數(shù)組,消耗的內(nèi)存空間一定是最多的绰精。

import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;

/**
 * <p>
 * 模擬熱點(diǎn)代碼
 *
 * @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萬長度的數(shù)組
     */
    private static void allocate() {
        array = new int[6 * 1000];
        array = new Integer[6 * 1000];
    }

    /**
     * 生成一個(gè)UUID
     */
    private static void hotmethod3() {
        ArrayList<String> list = new ArrayList<>();
        UUID uuid = UUID.randomUUID();
        String str = uuid.toString().replace("-", "");
        list.add(str);
    }

    /**
     * 數(shù)字累加
     */
    private static void hotmethod2() {
        value++;
    }

    /**
     * 生成一個(gè)隨機(jī)數(shù)
     */
    private static void hotmethod1() {
        Random random = new Random();
        int anInt = random.nextInt();
    }
}

CPU 性能分析

運(yùn)行上面的程序撒璧,然后使用 JPS 命令查看 PID 信息。

?  develop jps
2800 Jps
2449 HotCode
2450 Launcher
805 RemoteMavenServer36
470 NutstoreGUI
699
?  develop

上面運(yùn)行的類名是 HotCode笨使,可以看到對(duì)應(yīng)的 PID 是 2449卿樱。

使用 ./profiler.sh -d 20 -f 2449.svg 2449 命令對(duì) 2449 號(hào)進(jìn)程采樣20秒,然后得到生成的 2449.svg 文件硫椰,然后我們使用瀏覽器打開這個(gè)文件繁调,可以看到 CPU 的使用火焰圖

CPU 使用火焰圖

關(guān)于火焰圖怎么看靶草,一言以蔽之:火焰圖里蹄胰,橫條越長,代表使用的越多奕翔,從下到上是調(diào)用堆棧信息裕寨。在這個(gè)圖里可以看到 main 方法上面的調(diào)用中 hotmethod3 方法的 CPU 使用是最多的,點(diǎn)擊這個(gè)方法糠悯。還可能看到更詳細(xì)的信息帮坚。

hotmethod3 CPU 火焰圖

可以看到 replace 方法占用的 CPU 最多,也是程序中性能問題所在互艾,是需要注意的地方。

Heap 內(nèi)存分析

還是上面運(yùn)行的程序讯泣,進(jìn)程 PID 還是 2449纫普,這次使用 -e 參數(shù)分析內(nèi)存使用情況。

命令:./profiler.sh -d 20 -e alloc -f 2449-alloc.svg 2449

命令的意思是收集進(jìn)程號(hào)是 2449 的進(jìn)程的內(nèi)存信息 20 秒好渠,然后輸出為 2449-alloc.svg 文件昨稼。20秒后得到 svg 文件使用瀏覽器打開,可以看到內(nèi)存分配情況拳锚。

內(nèi)存分配火焰圖

依舊是橫條越長假栓,代表使用的越多,從下到上是調(diào)用堆棧信息霍掺。從圖里可以看出來 main 方法調(diào)用的 allocate 方法使用的內(nèi)存最多匾荆,這個(gè)方法里的 Integer 類型數(shù)組占用的內(nèi)存又最多拌蜘,為 71%。

文中測試代碼已經(jīng)上傳到 Github:https://github.com/niumoo/lab-notes

<完>
個(gè)人網(wǎng)站:https://www.codingme.net
如果你喜歡這篇文章牙丽,可以關(guān)注公眾號(hào)简卧,一起成長。
關(guān)注公眾號(hào)回復(fù)資源可以沒有套路的獲取全網(wǎng)最火的的 Java 核心知識(shí)整理&面試核心資料烤芦。

image

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末举娩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子构罗,更是在濱河造成了極大的恐慌铜涉,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遂唧,死亡現(xiàn)場離奇詭異骄噪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蠢箩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門链蕊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谬泌,你說我怎么就攤上這事滔韵。” “怎么了掌实?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵陪蜻,是天一觀的道長。 經(jīng)常有香客問我贱鼻,道長宴卖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任邻悬,我火速辦了婚禮症昏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘父丰。我一直安慰自己肝谭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布蛾扇。 她就那樣靜靜地躺著攘烛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镀首。 梳的紋絲不亂的頭發(fā)上坟漱,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音更哄,去河邊找鬼芋齿。 笑死腥寇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沟突。 我是一名探鬼主播花颗,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼惠拭!你這毒婦竟也來了扩劝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤职辅,失蹤者是張志新(化名)和其女友劉穎棒呛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體域携,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡簇秒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秀鞭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趋观。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锋边,靈堂內(nèi)的尸體忽然破棺而出皱坛,到底是詐尸還是另有隱情,我是刑警寧澤豆巨,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布剩辟,位于F島的核電站,受9級(jí)特大地震影響往扔,放射性物質(zhì)發(fā)生泄漏贩猎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一萍膛、第九天 我趴在偏房一處隱蔽的房頂上張望吭服。 院中可真熱鬧,春花似錦卦羡、人聲如沸噪馏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓶颠,卻和暖如春拟赊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粹淋。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工吸祟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瑟慈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓屋匕,卻偏偏與公主長得像葛碧,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子过吻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容