4、性能分析工具链:系统级工具与应用级工具

性能调优这件事,说白了就是「找瓶颈」。

但瓶颈不会自己跳出来告诉你它在哪。你得有趁手的工具,一层一层往下挖。我个人习惯把工具分成两类:系统级的,和应用级的。系统级工具帮你回答「机器怎么了」,应用级工具帮你回答「代码怎么了」。

今天我们就来聊聊这两类工具怎么用,以及我踩过的那些坑。

核心思路:先系统后应用,先宏观后微观。别一上来就开Arthas,先看看是不是CPU打满了。

性能分析工具链全景图 系统级工具 应用级工具 top vmstat iostat netstat Arthas JProfiler SkyWalking 先看系统级,定位到问题层面 再用应用级,定位到具体代码 由外到内,层层递进

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:服务和服务中的接口

我一般这么用:

  1. 在 SkyWalking UI 里找到慢的 Trace
  2. 看哪个 Span 耗时最长
  3. 点进去看具体的调用详情和异常信息

有一次线上反馈下单慢,我打开 SkyWalking 一看,发现订单服务调用库存服务时,有一个 Redis 操作耗时 800ms。再往下查,发现是库存服务的 Redis 连接池配置太小,请求在排队。

我的建议:SkyWalking 的探针开销很小,生产环境建议全量接入。别等到出问题了再想着加,那时候已经晚了。

4.3 工具链的配合使用

这些工具不是孤立的。我一般按这个流程来:

  1. top + vmstat:快速判断是 CPU、内存还是 IO 的问题
  2. iostat / netstat:如果是 IO 或网络问题,进一步定位
  3. Arthas:如果是应用自身问题,attach 上去看线程和调用
  4. SkyWalking:如果是分布式环境,看链路追踪
  5. JProfiler:本地复现,做深度分析

你想想看,如果一上来就用 JProfiler 去连生产环境,那既不现实也不安全。但如果你先用 top 和 vmstat 缩小范围,再用 Arthas 精准定位,效率就高多了。

最后提醒一句:工具只是辅助,核心还是你对系统的理解。别沉迷于工具的花哨功能,先把基础命令用熟。我见过有人用 Arthas 的 ognl 玩出花来,但连基本的 CPU 飙高都定位不到——这就本末倒置了。

好了,工具链就聊到这。记住一个原则:先系统后应用,先宏观后微观。下次遇到性能问题,别慌,按这个顺序来,大概率能快速找到根因。

可可精品资料库