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

简介: 超好用的自带火焰图的 Java 性能分析工具 Async-profiler 了解一下

image.png

火焰图


如果你经常遇到 Java 线上性能问题束手无策,看着线上服务 CPU 飙升一筹莫展,发现内存不断泄露满脸茫然。别慌,这里有一款低开销、自带火焰图、让你大呼好用的 Java 性能分析工具 - async-profiler


最近 Arthas 性能分析工具上线了火焰图分析功能,Arthas 使用 async-profiler 生成 CPU/内存火焰图进行性能分析,弥补了之前内存分析的不足。在 Arthas 上使用还是比较方便的,使用方式可以看官方文档。这篇文章介绍 async-profiler 相关内容。


Arthas 火焰图官方文档:https://alibaba.github.io/arthas/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 安装


下载 async-profiler 工具可以在官方的 Github 上直接下载编译好的文件,如果你就是想体验手动挡的感觉,也可以克隆项目,手动编译一下,不得不说这个工具十分的易用,我在手动编译的过程十分顺滑,没有出现任何问题。


如果你想下载编译好的,可以到这里下载。


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


如果想体验手动挡的感觉,可以克隆整个项目,进项项目编译。


手动编译的环境要求。


  • JDK
  • GCC


下面是手动安装的操作命令。


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


执行 make 命令编译后会在项目的目录下生成一个 build 文件夹,里面存放着编译的结果。下面是我手动编译的过程输出。


➜  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 一起设置引导类路径
1 个警告
/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/jar cvf build/async-profiler.jar -C build/classes .
已添加清单
正在添加: one/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: one/profiler/(输入 = 0) (输出 = 0)(存储了 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 使用


运行项目里的 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


常用的使用的几个步骤:


  1. 查看 java 进程的 PID(可以使用 jps )。
  2. 使用 ./profiler.sh start
    开始采样。
  3. 使用 ./profiler.sh status
    查看已经采样的时间。
  4. 使用 ./profiler.sh stop
    停止采样,输出结果。


这种方式使用起来多费劲啊,而且最后输出的是文本结果,看起来更是费劲,为了不那么费劲,可以使用帮助里给的采样后生成 SVG 文件例子。


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


这个命令的意思是,对 PID 为 3456 的 java 进程采样 30 秒,然后生成 profile.svg 结果文件。


默认情况下是分析 CPU 性能,如果要进行其他分析,可以使用 -e 参数。


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


可以看到支持的分析事件有 CPU、Alloc、Lock、Cache-misses 。


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 性能分析


运行上面的程序,然后使用 JPS 命令查看 PID 信息。


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


上面运行的类名是 HotCode,可以看到对应的 PID 是 2449。


使用 ./profiler.sh -d 20 -f 2449.svg 2449 命令对 2449 号进程采样20秒,然后得到生成的 2449.svg 文件,然后我们使用浏览器打开这个文件,可以看到 CPU 的使用火焰图


image.png


CPU 使用火焰图


关于火焰图怎么看,一言以蔽之:火焰图里,横条越长,代表使用的越多,从下到上是调用堆栈信息。在这个图里可以看到 main 方法上面的调用中 hotmethod3 方法的 CPU 使用是最多的,点击这个方法。还可能看到更详细的信息。


image.png


hotmethod3 CPU 火焰图


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


Heap 内存分析


还是上面运行的程序,进程 PID 还是 2449,这次使用 -e 参数分析内存使用情况。


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

命令的意思是收集进程号是 2449 的进程的内存信息 20 秒,然后输出为 2449-alloc.svg 文件。20秒后得到 svg 文件使用浏览器打开,可以看到内存分配情况。


image.png


内存分配火焰图


依旧是横条越长,代表使用的越多,从下到上是调用堆栈信息。从图里可以看出来 main 方法调用的 allocate 方法使用的内存最多,这个方法里的 Integer 类型数组占用的内存又最多,为 71%。



相关文章
|
9天前
|
缓存 JavaScript Java
常见java OOM异常分析排查思路分析
Java虚拟机(JVM)遇到内存不足时会抛出OutOfMemoryError(OOM)异常。常见OOM情况包括:1) **Java堆空间不足**:大量对象未被及时回收或内存泄漏;2) **线程栈空间不足**:递归过深或大量线程创建;3) **方法区溢出**:类信息过多,如CGLib代理类生成过多;4) **本机内存不足**:JNI调用消耗大量内存;5) **GC造成的内存不足**:频繁GC但效果不佳。解决方法包括调整JVM参数(如-Xmx、-Xss)、优化代码及使用高效垃圾回收器。
75 15
常见java OOM异常分析排查思路分析
|
15天前
|
缓存 JavaScript Java
常见java OOM异常分析排查思路分析
Java虚拟机(JVM)遇到 OutOfMemoryError(OOM)表示内存资源不足。常见OOM情况包括:1) **Java堆空间不足**:内存被大量对象占用且未及时回收,或内存泄漏;解决方法包括调整JVM堆内存大小、优化代码及修复内存泄漏。2) **线程栈空间不足**:单线程栈帧过大或频繁创建线程;可通过优化代码或调整-Xss参数解决。3) **方法区溢出**:运行时生成大量类导致方法区满载;需调整元空间大小或优化类加载机制。4) **本机内存不足**:JNI调用或内存泄漏引起;需检查并优化本机代码。5) **GC造成的内存不足**:频繁GC但效果不佳;需优化JVM参数、代码及垃圾回收器
常见java OOM异常分析排查思路分析
|
16天前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
12天前
|
监控 IDE Java
【Java性能调优新工具】JDK 22性能分析器:深度剖析,优化无死角!
【9月更文挑战第9天】JDK 22中的性能分析器为Java应用的性能调优提供了强大的支持。通过深度集成、全面监控、精细化分析和灵活报告生成等核心优势,性能分析器帮助开发者实现了对应用性能的全面掌控和深度优化。在未来的Java开发过程中,我们期待性能分析器能够继续发挥重要作用,为Java应用的性能提升贡献更多力量。
|
12天前
|
Java 数据处理
技术分享:高效与灵活并存——Java版通用树形结构转换工具的实现与应用
在软件开发中,树形结构的数据表现形式无处不在,从文件系统的目录树到组织架构的部门树,再到各类产品的分类结构。处理这些具有层级关系的数据时,将其转换为树形结构以便展示和操作显得尤为重要。Java作为一门成熟的编程语言,虽然提供了强大的集合框架,但并未直接提供树形结构转换的内置工具。因此,开发一个高效且灵活的通用树形结构转换工具成为许多项目中的必备需求。
23 2
|
12天前
|
安全 Java 数据安全/隐私保护
- 代码加密混淆工具-Java 编程安全性
在Java编程领域,保护代码安全与知识产权至关重要。本文探讨了代码加密混淆工具的重要性,并介绍了五款流行工具:ProGuard、DexGuard、Jscrambler、DashO 和 Ipa Guard。这些工具通过压缩、优化、混淆和加密等手段,提升代码安全性,保护知识产权。ProGuard 是开源工具,用于压缩和混淆Java代码;DexGuard 专为Android应用程序设计,提供强大加密功能;Jscrambler 基于云,保护Web和移动应用的JavaScript及HTML5代码;DashO 支持多种Java平台和
29 1
|
25天前
|
Dubbo Java 关系型数据库
Java SPI机制分析
文章深入分析了Java SPI机制,以JDBC为例,详细探讨了服务提供者接口的发现、加载过程,并提供了一个序列化服务的实战示例,展示了如何使用ServiceLoader进行服务发现和扩展。
16 3
|
25天前
|
监控 算法 安全
Java并发编程案例分析:死锁的检测与解决
Java并发编程案例分析:死锁的检测与解决
21 2
|
26天前
|
安全 Java API
精通 Java 后台开发:案例分析与实践
精通 Java 后台开发:案例分析与实践
31 2
|
1月前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。