4、性能分析工具链:系统级工具与应用级工具
性能调优这件事,说白了就是「找瓶颈」。
但瓶颈不会自己跳出来告诉你它在哪。你得有趁手的工具,一层一层往下挖。我个人习惯把工具分成两类:系统级的,和应用级的。系统级工具帮你回答「机器怎么了」,应用级工具帮你回答「代码怎么了」。
今天我们就来聊聊这两类工具怎么用,以及我踩过的那些坑。
核心思路:先系统后应用,先宏观后微观。别一上来就开Arthas,先看看是不是CPU打满了。
4.1 系统级工具:先看机器扛不扛得住
你想想看,如果一台机器的CPU已经100%了,你还在那分析代码逻辑,那不是白费劲吗?所以我的习惯是:先看系统,再看应用。
4.1.1 top:第一眼印象
top 是我每次上服务器第一个敲的命令。它告诉你最直观的信息:CPU忙不忙,内存够不够,哪个进程在吃资源。
我记得有一次线上告警,用户反馈接口响应慢。我 ssh 上去先敲了个 top,好家伙,CPU 的 us 列飙到了 85%,而且有个 Java 进程的 PID 特别扎眼。嗯,问题范围一下就锁定了。
常用的几个快捷键和参数:
top -p PID:只看某个进程shift + P:按CPU使用率排序shift + M:按内存使用率排序1:展开所有CPU核心
小技巧:别只看 load average。有时候 load 高但 CPU 不高,那可能是 IO 在排队。这时候就要换工具了。
4.1.2 vmstat:看系统整体健康度
vmstat 是我个人非常喜欢的一个工具。它不像 top 那么「吵」,但信息量很大。
用法很简单:
# 每2秒输出一次,共输出5次
vmstat 2 5
输出里我最关注这几列:
| 列名 | 含义 | 我关注的阈值 |
|---|---|---|
| r | 运行队列中的进程数 | 持续大于CPU核数 → 有瓶颈 |
| b | 不可中断睡眠的进程数 | 持续大于0 → 可能是IO瓶颈 |
| si/so | 交换内存的换入换出 | 大于0 → 内存不够了 |
| us/sy | 用户态/内核态CPU时间 | sy过高 → 系统调用频繁 |
我曾经遇到过一个案例,r 列的值一直维持在 20 以上,但 CPU 只有 8 核。这说明有大量线程在竞争CPU,不是单纯的CPU不够,而是线程数开太多了。后来优化了线程池大小,问题解决。
4.1.3 iostat:磁盘到底慢不慢
如果你的应用慢,但CPU和内存都正常,那十有八九是磁盘IO的问题。iostat 就是干这个的。
# 查看磁盘IO情况,每2秒一次
iostat -x 2
重点关注:
- %util:磁盘利用率。接近100%说明磁盘很忙。
- await:IO请求的平均等待时间。超过几十毫秒就算高了。
- svctm:IO服务的实际处理时间。如果 await 远大于 svctm,说明有排队。
注意:%util 到 100% 不一定就是磁盘坏了。如果是 SSD,并发能力强,%util 到 100% 可能还能扛。但如果是机械盘,%util 到 100% 基本就卡死了。
4.1.4 netstat:网络连接排查
网络问题有时候比CPU和磁盘更隐蔽。netstat 是我排查连接泄漏的利器。
常用命令:
# 查看所有TCP连接状态
netstat -ant
# 统计各种状态的连接数
netstat -ant | awk '{print $6}' | sort | uniq -c
我最怕看到的是 TIME_WAIT 和 CLOSE_WAIT 堆积。
- TIME_WAIT 过多:一般是短连接太多,可以优化连接复用或调整内核参数。
- CLOSE_WAIT 过多:这通常是代码问题,服务端没正确关闭连接。我曾经排查过一个案例,发现是 HTTP 客户端的响应流没读完,导致连接一直挂着。
4.2 应用级工具:深入代码层面
系统级工具帮你定位到「哪个进程有问题」。接下来,应用级工具帮你定位到「哪行代码有问题」。
4.2.1 Arthas:线上诊断神器
Arthas 是我线上排查问题的首选。它不需要重启应用,不需要改代码,attach 上去就能干活。
我最常用的几个命令:
| 命令 | 用途 | 我的使用场景 |
|---|---|---|
| dashboard | 实时查看线程、内存、GC信息 | 一上去先敲这个,看全局 |
| thread | 查看线程堆栈,定位死锁或阻塞 | 接口卡住时,看哪个线程在等 |
| watch | 观测方法调用参数和返回值 | 排查参数不对或返回异常 |
| trace | 追踪方法调用链路和耗时 | 找哪个子调用最慢 |
| ognl | 在线执行表达式 | 临时修改配置或调用某个方法 |
举个例子,有一次线上某个接口偶尔超时,用 trace 命令一追踪,发现是某个 Redis 查询偶尔会卡 2 秒。后来定位到是 Redis 的 big key 问题。
# 追踪 com.example.service.UserService.getUser 方法
trace com.example.service.UserService getUser
避坑指南:我曾经在生产环境用 watch 命令监控一个高频方法,结果打印太多,反而把 CPU 打高了。后来我加了条件过滤,只监控特定参数的情况。
4.2.2 JProfiler:本地压测分析利器
Arthas 适合线上应急,但如果你想做深度的性能分析,比如内存泄漏、CPU热点,JProfiler 更合适。它是个 GUI 工具,信息展示非常直观。
我一般在本地压测时用 JProfiler:
- CPU 视图:看哪个方法调用次数最多、耗时最长。我习惯先看「热点方法」,往往能一眼看出问题。
- 内存视图:看对象分配情况。如果某个对象数量异常多,那大概率是内存泄漏。
- 线程视图:看线程状态和锁竞争。死锁在这里一目了然。
我记得有一次排查内存泄漏,JProfiler 的堆遍历功能帮我找到了一个 HashMap 在不断膨胀,里面存的是未清理的缓存数据。嗯,代码里少了一个 remove 调用。
4.2.3 SkyWalking:分布式链路追踪
微服务架构下,一个请求可能要经过五六个服务。这时候你再一台一台机器看日志,效率太低了。SkyWalking 就是干这个的——把整个调用链路串起来。
核心概念:
- Trace:一次请求的完整链路
- Span:链路中的每一个调用片段
- Service/Endpoint:服务和服务中的接口
我一般这么用:
- 在 SkyWalking UI 里找到慢的 Trace
- 看哪个 Span 耗时最长
- 点进去看具体的调用详情和异常信息
有一次线上反馈下单慢,我打开 SkyWalking 一看,发现订单服务调用库存服务时,有一个 Redis 操作耗时 800ms。再往下查,发现是库存服务的 Redis 连接池配置太小,请求在排队。
我的建议:SkyWalking 的探针开销很小,生产环境建议全量接入。别等到出问题了再想着加,那时候已经晚了。
4.3 工具链的配合使用
这些工具不是孤立的。我一般按这个流程来:
- top + vmstat:快速判断是 CPU、内存还是 IO 的问题
- iostat / netstat:如果是 IO 或网络问题,进一步定位
- Arthas:如果是应用自身问题,attach 上去看线程和调用
- SkyWalking:如果是分布式环境,看链路追踪
- JProfiler:本地复现,做深度分析
你想想看,如果一上来就用 JProfiler 去连生产环境,那既不现实也不安全。但如果你先用 top 和 vmstat 缩小范围,再用 Arthas 精准定位,效率就高多了。
最后提醒一句:工具只是辅助,核心还是你对系统的理解。别沉迷于工具的花哨功能,先把基础命令用熟。我见过有人用 Arthas 的 ognl 玩出花来,但连基本的 CPU 飙高都定位不到——这就本末倒置了。
好了,工具链就聊到这。记住一个原则:先系统后应用,先宏观后微观。下次遇到性能问题,别慌,按这个顺序来,大概率能快速找到根因。