一、性能剖析概述
什么是性能剖析
性能剖析,说白了就是给程序做「体检」。
你写了一段代码,跑起来了,功能也对了。但你真的知道它跑得怎么样吗?
我刚开始做开发那会儿,总觉得「能跑就行」。直到有一次线上服务突然卡死,查了半天才发现是一个循环里做了大量字符串拼接。嗯,从那以后,我就养成了定期做性能剖析的习惯。
性能剖析的核心,就是回答三个问题:
- 慢在哪 —— 哪个函数、哪行代码耗时最长
- 吃多少 —— CPU、内存、IO、网络分别用了多少
- 为什么 —— 是算法问题?还是资源争抢?
你可以把它想象成医生用的听诊器。没有它,你只能靠猜。有了它,你就能精准定位病灶。
为什么需要性能剖析
你可能会问:「我代码写得挺规整的,有必要吗?」
我个人经验是:有必要,而且非常有必要。
举个例子。我曾经接手过一个数据处理系统,每天跑批要花6个小时。业务方天天催,开发团队也改了好几次,效果都不明显。后来我用性能剖析工具一查,发现80%的时间都花在了一个数据库查询上——索引没建对。改完之后,跑批时间直接降到40分钟。
你看,没有数据支撑的优化,就是瞎优化。
性能剖析能帮你做到三件事:
- 量化问题 —— 不说「有点慢」,而是说「这个接口平均耗时2.3秒」
- 定位根因 —— 不说「可能是数据库的问题」,而是说「这条SQL全表扫描了100万行」
- 验证效果 —— 优化前测一次,优化后测一次,数据说话
核心观点:没有性能剖析的优化,等于蒙着眼睛修车。
性能剖析的核心指标
做性能剖析,你得知道看什么指标。我一般关注四个维度:CPU、内存、IO、网络。这四样,缺一不可。
1. CPU
CPU指标反映的是「计算密集度」。说白了,就是你的程序在多大程度上「烧」CPU。
关键指标包括:
| 指标 | 说明 | 警戒线 |
|---|---|---|
| CPU使用率 | CPU被占用的百分比 | 持续 > 80% 需关注 |
| 用户态/内核态占比 | 区分是业务代码还是系统调用消耗的CPU | 内核态 > 30% 可能有系统调用瓶颈 |
| 上下文切换次数 | 线程切换的频率 | 每秒 > 10万次需排查 |
| 平均负载 | 正在运行+等待运行的进程数 | 超过CPU核数说明有排队 |
我记得有一次排查一个Java服务,CPU使用率一直飙在95%以上。用top一看,发现是GC线程在疯狂跑。再深入一看,堆内存设置太小,对象频繁晋升到老年代,触发Full GC。调大堆内存后,CPU直接降到20%。
小技巧:排查CPU问题时,先用top看整体,再用perf top或火焰图定位热点函数。
2. 内存
内存指标关注的是「空间使用效率」。内存泄漏、频繁GC、大对象分配,都是常见问题。
关键指标:
| 指标 | 说明 | 警戒线 |
|---|---|---|
| 内存使用率 | 已用内存占总内存的比例 | 持续 > 85% 需关注 |
| GC频率和停顿时间 | 垃圾回收的频次和耗时 | Full GC > 1次/分钟 或停顿 > 1秒 |
| 堆内/堆外内存 | Java中区分JVM堆和直接内存 | 堆外内存增长异常需排查 |
| 内存泄漏增长率 | 内存使用是否随时间持续增长 | 每次GC后内存不回落,大概率泄漏 |
我曾经遇到过一个诡异的问题:服务运行两天后就会OOM。用jmap dump了堆快照,发现一个HashMap里存了上千万个对象,而且只增不减。查代码发现是缓存没设过期策略。嗯,这种坑踩过一次就记住了。
注意:内存泄漏往往不是一下子爆出来的,而是慢慢「温水煮青蛙」。建议对内存使用做持续监控,而不是出问题了才看。
3. IO
IO指标关注的是「数据读写效率」。磁盘IO慢,往往是整个系统的瓶颈。
关键指标:
| 指标 | 说明 | 警戒线 |
|---|---|---|
| IOPS | 每秒读写次数 | 接近磁盘上限需关注 |
| 吞吐量 | 每秒读写的数据量 | 接近带宽上限需关注 |
| IO等待时间 | 进程等待IO完成的时间 | %iowait > 30% 说明磁盘是瓶颈 |
| IO队列长度 | 等待处理的IO请求数 | 队列持续 > 磁盘并发数 |
你想想看,CPU处理一个指令是纳秒级的,磁盘寻道却是毫秒级的。差了上百万倍。所以IO优化往往是性价比最高的优化方向。
4. 网络
网络指标关注的是「数据传输效率」。微服务架构下,网络问题尤其常见。
关键指标:
| 指标 | 说明 | 警戒线 |
|---|---|---|
| 带宽使用率 | 已用带宽占总带宽的比例 | 持续 > 70% 需关注 |
| 延迟 | 请求从发出到收到响应的时间 | P99 > 500ms 需排查 |
| 丢包率 | 丢失的数据包占比 | 丢包率 > 0.1% 说明网络不稳定 |
| 连接数 | 当前建立的TCP连接数 | 接近系统上限需关注 |
我记得有一次排查一个接口超时问题,代码层面怎么看都没毛病。后来用tcpdump抓包一看,发现每次请求都要做三次握手——原来是连接池没配好,每次请求都新建连接。改成连接复用后,延迟从800ms降到了50ms。
四个指标的关系
这四个指标不是孤立的。它们之间会相互影响:
- CPU高,可能是因为内存不足导致频繁GC
- IO慢,会导致CPU大量时间花在等待上
- 网络延迟高,会让线程长时间阻塞,间接增加上下文切换
所以做性能剖析时,我建议你四个维度一起看,不要只盯着一个。
总结一下:性能剖析就是给程序做体检。四个核心指标——CPU、内存、IO、网络——就像体检报告上的四项关键数据。哪个指标异常,就说明哪个环节有问题。我建议你把这四个指标刻在脑子里,每次排查问题都过一遍。
我的习惯:接到性能问题,先看CPU和内存,再看IO和网络。因为前两个最容易暴露问题,后两个往往需要更深入的排查手段。当然,具体问题具体分析,没有固定的顺序。
好了,这一章就聊到这儿。下一章我们开始动手,看看怎么用具体的工具来采集这些指标数据。