深色模式
持续性能剖析 Profiling
摘要:指标告诉你慢了,链路告诉你哪一跳慢,而 Profiling 告诉你代码里哪一行慢。本文讲清 CPU 与内存剖析的区别,带你用 perf/async-profiler 生成火焰图,并搭建常驻的持续剖析能力用于随时回溯任意时点的性能状态。
适用环境
bash
# Linux perf(内核自带,需对应内核版本的调试符号)
perf version 2>/dev/null || sudo yum install -y perf
# Java 场景推荐 async-profiler
ls /opt/async-profiler 2>/dev/null || echo "需下载 async-profiler"
# Go/Python 自带 pprof
go version 2>/dev/null; python3 -c "import pstats" 2>/dev/null && echo "python ok"
# 检查 perf_event 权限
cat /proc/sys/kernel/perf_event_paranoid # <=2 才允许非 root 采集1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
操作步骤
1. 理解四类剖析
| 类型 | 回答的问题 | 典型开销 |
|---|---|---|
| CPU | 哪些函数吃 CPU | 低(1%-5%) |
| 内存分配 | 哪些对象频繁分配 | 中 |
| 阻塞/Off-CPU | 线程在等什么(锁、IO) | 中 |
| 锁竞争 | 哪些锁最抢手 | 中高 |
2. 用 perf 采集并生成火焰图
bash
PID=$(pgrep -f 'java.*app.jar' | head -1)
sudo perf record -F 99 -g -p $PID -- sleep 30 # 99Hz 采样 30 秒
sudo perf script > /tmp/perf.out1
2
3
2
3
bash
# 用 FlameGraph 工具生成 SVG
git clone --depth 1 https://github.com/brendangregg/FlameGraph.git /opt/FlameGraph
/opt/FlameGraph/stackcollapse-perf.pl /tmp/perf.out > /tmp/perf.folded
/opt/FlameGraph/flamegraph.pl /tmp/perf.folded > /tmp/cpu-flamegraph.svg1
2
3
4
2
3
4
3. Java 用 async-profiler(开箱即用,无需 perf 权限)
bash
# 到 Releases 页核对版本后下载解压
tar -xzf async-profiler-*-linux-x64.tar.gz -C /opt
# CPU 剖析 30 秒,输出火焰图
/opt/async-profiler/bin/asprof -d 30 -e cpu -f /tmp/java-cpu.html $PID
# 内存分配剖析
/opt/async-profiler/bin/asprof -d 30 -e alloc -f /tmp/java-alloc.html $PID
# 阻塞剖析(线程在等什么)
/opt/async-profiler/bin/asprof -d 30 -e wall -f /tmp/java-wall.html $PID1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
4. Go 应用用 pprof
bash
curl -s "http://127.0.0.1:6060/debug/pprof/profile?seconds=30" -o /tmp/cpu.pb.gz
go tool pprof -http=:8081 /tmp/cpu.pb.gz # 浏览器查看火焰图1
2
2
5. 搭建持续剖析(常驻低开销采集,随时可回溯)
bash
# 以 Pyroscope/Parca 这类持续剖析方案为例:agent 常驻采集并上报
# 启动 agent 后配置抓取目标(按官方文档部署)
curl -fsS http://127.0.0.1:4040/ready # Pyroscope 健康检查1
2
3
2
3
yaml
# 应用侧接入示例(Java agent 参数思路)
-javaagent:/opt/pyroscope/agent.jar
-Dpyroscope.application.name=demo-app
-Dpyroscope.server.address=http://127.0.0.1:4040
-Dpyroscope.format=jfr1
2
3
4
5
2
3
4
5
6. 做性能回归对比
bash
# 升级前后各采集一次,对比火焰图中新增/变宽的栈帧
/opt/async-profiler/bin/asprof -d 60 -e cpu -f /tmp/before.html $PID
# ... 发布新版本 ...
/opt/async-profiler/bin/asprof -d 60 -e cpu -f /tmp/after.html $PID1
2
3
4
2
3
4
验证
bash
# 1) perf 可采集(无权限会报 Permission denied)
sudo perf stat -p $PID -- sleep 3
# 2) 火焰图文件已生成且非空
ls -lh /tmp/cpu-flamegraph.svg /tmp/java-cpu.html
# 3) 折叠栈里能看到业务函数而非只有 [unknown]
head -5 /tmp/perf.folded
# 4) 持续剖析后端可查询
curl -s 'http://127.0.0.1:4040/api/apps' | head -c 2001
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
常见坑
WARNING
Java 进程用 perf 采集常看到大量 [unknown] 栈帧,因为 JIT 代码没有符号。需要加 -XX:+PreserveFramePointer,或直接使用 async-profiler 这类 JVM 原生方案。
WARNING
采样频率越高越精确但开销越大。生产环境建议 CPU 剖析 99Hz、持续 30-60 秒即可定位绝大多数热点,不要长时间高频采集。
DANGER
剖析数据里包含完整的方法名、类名与调用栈,属于代码级敏感信息。分享火焰图前请确认可以对外披露;持续剖析平台的访问权限应与源码仓库同级管理。