上篇文章写完后,性能调优|成都核酸系统篇 收到了一些夸赞,让我有继续写下去的动力;
也有人提了一些意见,我也会尽可能满足。
linux系统上,可以直接使用 perf 工具采样数据,然后用火焰图工具生成火焰图。那么Java是不是也可以使用perf呢?是的,也可以,但是需要安装一个perf-map-agent,把底层堆栈转换为Java可见代码,然后通过FlameGraph生成火焰图(profile是另外一个bcc的工具,性能消耗比perf还要低,也可以用CPU占用剖析)。
这里我们介绍一个更简单易用的工具 async-profiler。它是一款开源的 Java 性能分析工具,原理是基于 HotSpot 的 API,以微乎其微的性能开销收集程序运行中的堆栈信息、内存分配等信息进行分析。
async-profiler 不使用侵入性的技术,例如字节码检测工具或者探针检测等,这也说明 async-profiler 的内存分配分析像 CPU 性能分析一样,不会产生太大的性能开销,同时也不用写出庞大的堆栈文件再去进行进一步处理,。
async-profile 目前支持 Linux 和 macOS 平台(macOS 下只能分析用户空间的代码)。
async-profiler 可以跟踪以下类型的事件:
下载地址:
https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.8.3/async-profiler-2.8.3-linux-x64.tar.gz
cd async-profiler-2.8.3-linux-x64
./profile.sh
./profiler.sh -d 30 -f s1.html 1189878
其中火焰图里,横条越长,代表使用的越多,从下到上是调用堆栈信息。在这个图里可以看到序列化方法的 CPU 使用是最多的,点击这个方法。还可能看到关于这个方法更详细的信息。
./profiler.sh -d 30 -e wall -f s2.html 1189878
./profiler.sh -d 30 -e alloc -f s3.html 1189878
从主机进行分析时,pid应该是主机命名空间中的Java进程ID。使用ps aux | grep java
或docker top<container>
查找进程ID。
async-profiler应该由特权用户从主机运行 - 它将自动切换到正确的pid/装载命名空间,并更改用户凭据以匹配目标进程。还要确保目标容器可以通过与主机上相同的绝对路径访问libasyncProfiler.so
。
默认情况下,Docker container限制对perf_event_open syscall
的访问。因此,为了允许在容器中进行分析,您需要修改seccomp配置文件,或者使用--security-opt seccomp=unconfined
选项完全禁用它。此外需要添加 --cap-add SYS_ADMIN
。
详细请参考:https://github.com/jvm-profiling-tools/async-profiler#troubleshooting
在服务运行过程中,通过Java自带jstack工具转储堆栈,通常可以看出服务中所有线程CPU占用,这种方式虽然简单,但是很难看到CPU占用全貌。具体步骤如下所示:
有效提高服务性能的方法就是通过火焰图或者其它工具找到生产环境中的热点代码,如果这块代码是CPU/IO密集型的,意味着这块代码经常占用CPU/IO,就要查看这块代码的CPU/IO消耗是否合理?是否可以通过修改算法、升级版本来降低CPU消耗,然后修改部署,再次测试,如此反复。