自带火焰图的Java性能分析工具Async-profiler


如果你经常遇到 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 使用是最多的,点击这个方法。还可能看到更详细的信息。

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

Heap 内存分析

还是上面运行的程序,这次分析内存使用情况。

profiler start --event alloc

得到的 svg 文件使用浏览器打开,可以看到内存分配情况。

内存分配火焰图

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

文中测试代码已经上传到 Github:https://github.com/niumoo/lab-notes

站在巨人肩膀上摘苹果

https://blog.csdn.net/u013735734/article/details/103452212

https://www.cnblogs.com/leihuazhe/p/11630466.html

原文地址:https://www.cnblogs.com/yuluoxingkong/p/15076549.html