3. 性能剖析工具概览:Linux性能工具、Java性能工具、Python性能工具、前端性能工具
说实话,性能优化这件事,工具选对了,你就成功了一半。我见过太多人一上来就对着代码瞎猜,结果折腾半天连瓶颈在哪都没摸到。你想想看,没有温度计你怎么知道发烧了?没有听诊器你怎么知道肺里有痰?性能工具就是我们的温度计和听诊器。
这一章,我会带你快速过一遍四个领域的常用工具。别指望一口气全记住,先混个脸熟,知道什么场景该用什么工具就行。我在项目中踩过的坑,也会一并告诉你。
3.1 Linux 性能工具:系统级排查的基石
不管你的应用是用 Java、Python 还是 Go 写的,最终都跑在 Linux 上。所以,Linux 性能工具是基本功。我个人习惯把这套工具分成两类:「看全局的」和「查细节的」。
3.1.1 全局视角:top、htop、vmstat
top 命令,老牌工具了。一上来先看 load average,三个数字分别代表 1 分钟、5 分钟、15 分钟的平均负载。如果 1 分钟远高于 15 分钟,说明系统正在经历突发压力。
我建议你装个 htop,它是 top 的增强版。颜色更友好,还能用鼠标操作。嗯,这里要注意:htop 默认不显示线程,你得按 H 键切换。
- %us:用户态 CPU 占用。高的话说明应用在算东西。
- %sy:内核态 CPU 占用。高的话说明系统调用频繁,或者有锁竞争。
- %wa:等待 I/O 的时间。高的话说明磁盘或网络是瓶颈。
- %st:被虚拟机偷走的时间。云服务器上常见,说明宿主机超卖了。
vmstat 1 是我最常用的实时监控命令。每隔 1 秒输出一次。重点关注 r(运行队列)、b(阻塞队列)、si/so(交换分区读写)。
# 每秒输出一次,共输出 5 次
vmstat 1 5
# 输出示例
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 123456 1024 2048576 0 0 10 20 500 800 12 3 85 0 0
我曾经遇到过一个案例,r 队列一直大于 CPU 核数,但 CPU 空闲率却很高。排查了半天,发现是某个进程在疯狂触发缺页中断,导致上下文切换爆炸。这就是 cs(context switch)列飙到几万的原因。
3.1.2 磁盘与网络:iostat、sar、netstat
iostat -x 1 可以看磁盘的详细情况。重点关注 %util 和 await。%util 接近 100% 说明磁盘已经饱和了。await 是 I/O 请求的平均等待时间,SSD 一般应该在 1ms 以内。
%util 骗过一次。某个 SSD 的 %util 只有 30%,但应用已经慢得不行了。后来发现是 await 高达 200ms,说明磁盘内部在做垃圾回收。所以,别只看一个指标。
sar 是个历史数据收集器。默认系统会每 10 分钟记录一次性能数据。你可以用 sar -u -f /var/log/sa/saXX 回溯昨天的 CPU 使用情况。这在排查「昨晚到底发生了什么」时特别好用。
网络方面,netstat -s 可以看协议栈的统计信息。比如 retransmitted 段数,如果这个值很高,说明网络丢包严重。
3.1.3 进程级诊断:strace、lsof、perf
strace -p PID 可以跟踪进程的系统调用。这是终极武器,但也是双刃剑。因为它会大幅降低进程性能,生产环境慎用。
我一般这样用:先 strace -c -p PID 统计系统调用的次数和耗时,找到最耗时的系统调用后,再用 strace -e trace=xxx -p PID 定向跟踪。
# 统计系统调用
strace -c -p 12345
# 只跟踪 read 和 write 调用
strace -e trace=read,write -p 12345
lsof 可以看进程打开了哪些文件。排查「too many open files」错误时必备。perf 是 Linux 内核自带的性能剖析工具,可以采样 CPU 的调用栈,定位热点函数。
perf top 命令可以实时显示 CPU 热点。如果某个函数占用了 30% 以上的 CPU,那基本就是优化目标了。
3.2 Java 性能工具:JVM 内部的探针
Java 应用有 JVM 这层虚拟机,所以工具也分两层:JVM 层和应用层。
3.2.1 JDK 自带工具:jstack、jmap、jstat、jcmd
这些工具都在 JDK 的 bin 目录下。我个人最常用的是 jstat,它可以实时监控 GC 情况。
# 查看 GC 统计,每 1 秒输出一次
jstat -gcutil PID 1000
# 输出示例
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 45.23 78.12 92.34 88.56 1234 12.345 5 0.678 13.023
jstack 可以打印线程堆栈。当 CPU 飙高时,用 jstack PID | grep -A 30 "RUNNABLE" 找到正在运行的线程,看看它在执行什么代码。
jmap -heap PID 可以查看堆内存的配置和使用情况。jmap -histo PID 可以列出所有对象的数量和占用内存,帮你找到内存泄漏的嫌疑对象。
jstack PID 连续 dump 三次,每次间隔 5 秒。然后对比三个 dump 文件,看哪些线程一直卡在同一个地方。那就是死锁或者长时间等待的线索。
3.2.2 可视化工具:VisualVM、JMC、Arthas
VisualVM 是 Oracle 官方提供的图形化工具。可以看 CPU、内存、线程、GC 的实时曲线。我习惯用它来观察 GC 的频率和耗时,如果 Full GC 频繁,那就要调堆内存大小了。
JMC(Java Mission Control) 是 Oracle 的商业工具,现在对 JDK 11+ 免费了。它的 飞行记录器(JFR) 功能可以在低开销下持续记录 JVM 的运行数据。生产环境也能用。
Arthas 是阿里巴巴开源的诊断工具。我最喜欢它的 watch 命令,可以实时查看某个方法的入参、返回值、异常,甚至还能动态修改代码逻辑。嗯,这个工具我强烈建议你装一个。
# 监控 com.example.service.UserService.getUser 方法
watch com.example.service.UserService getUser '{params, returnObj}' -x 2
3.3 Python 性能工具:从脚本到服务
Python 的性能工具相对轻量,但够用。我把它分成内置模块和第三方工具两类。
3.3.1 内置模块:timeit、cProfile、trace
timeit 适合微基准测试。比如你想比较列表推导式和 for 循环哪个快,用 timeit 一测便知。
import timeit
# 测试列表推导式
timeit.timeit('[x**2 for x in range(1000)]', number=10000)
# 测试 for 循环
timeit.timeit('''
result = []
for x in range(1000):
result.append(x**2)
''', number=10000)
cProfile 是 Python 自带的性能剖析器。它会记录每个函数的调用次数和耗时。我一般这样用:
python -m cProfile -o output.prof my_script.py
然后用 pstats 模块分析结果,或者用 snakeviz 生成火焰图。
cProfile 跑一次,找到最耗时的函数。然后针对那个函数,用 timeit 做微优化。这样效率最高。
3.3.2 第三方工具:py-spy、memory_profiler、line_profiler
py-spy 是一个采样剖析器,不需要修改代码,直接 attach 到运行中的进程。它支持生成火焰图,对排查 CPU 热点非常有用。
# 对运行中的进程进行采样
py-spy record -o flamegraph.svg --pid 12345
# 或者直接查看实时调用栈
py-spy top --pid 12345
memory_profiler 可以逐行分析内存使用。用 @profile 装饰器标记要分析的函数,然后运行 python -m memory_profiler my_script.py。
line_profiler 可以逐行分析代码的执行时间。它比 cProfile 更细粒度,能精确到每一行代码。
3.4 前端性能工具:浏览器端的显微镜
前端性能优化,说白了就是让页面加载更快、交互更流畅。工具主要集中在浏览器开发者工具和性能审计工具。
3.4.1 Chrome DevTools:Performance、Network、Lighthouse
Performance 面板是前端性能分析的核心。点击录制按钮,然后操作页面,结束后会生成一个时间线。重点关注:
- FPS:帧率。低于 30 说明有卡顿。
- CPU 火焰图:看哪些函数占用了大量 CPU。
- Layout / Paint:布局和绘制事件。频繁触发说明有强制回流。
Network 面板可以看每个请求的耗时。重点关注 TTFB(Time To First Byte),如果这个值超过 200ms,说明后端响应慢。另外,瀑布图里如果有大量请求串行加载,说明有资源依赖没处理好。
Lighthouse 是 Google 的自动化审计工具。它会生成一个性能报告,给出具体的优化建议。比如「图片太大,建议压缩」或者「未使用懒加载」。
| 指标 | 含义 | 目标值 |
|---|---|---|
| FCP | 首次内容绘制 | < 1.8s |
| LCP | 最大内容绘制 | < 2.5s |
| FID | 首次输入延迟 | < 100ms |
| CLS | 累计布局偏移 | < 0.1 |
3.4.2 其他工具:WebPageTest、Bundle Analyzer
WebPageTest 是一个在线工具,可以从全球多个节点测试你的页面加载速度。它比 Lighthouse 更详细,能模拟 3G 网络、慢速 CPU 等场景。
Bundle Analyzer 用于分析打包后的 JavaScript 文件。如果你用 Webpack,可以配置 webpack-bundle-analyzer 插件,生成一个交互式的树状图,直观地看到哪些模块占用了大量体积。
resolve.alias 统一版本。
3.5 知识体系总览
下面这张图,是我自己总结的性能工具知识体系。你可以把它当作一个「工具地图」,遇到问题先看属于哪个领域,再去找对应的工具。
最后说一句:工具是死的,人是活的。别为了用工具而用工具。我见过有人把 strace 挂在生产环境上跑了半小时,结果把数据库连接池打爆了。嗯,那就是反面教材。
记住一个原则:先问「我要解决什么问题」,再选「用什么工具」。这样你才不会在工具的海洋里迷失方向。