性能问题分类:常见性能问题类型及其特征

性能问题的表象千差万别,但根因往往集中在有限的几类资源竞争或调度失衡上。面对一个“慢”或“卡”的系统,第一步不是盲目调参,而是先判断问题属于哪一类型。本章将CPU、内存、IO、网络、锁竞争五类核心性能问题逐一拆解,帮助建立“症状→特征→定位方向”的快速映射。

核心原理:性能问题的本质是资源供需失衡

任何计算系统都可抽象为“请求—资源—调度”模型。当请求量超过资源处理能力,或资源被低效占用、互相争抢时,性能就会劣化。五类问题分别对应不同资源的耗尽或低效:

  • CPU问题:计算资源成为瓶颈,线程在Runnable状态排队,或频繁上下文切换。
  • 内存问题:堆空间不足、GC频繁、对象泄漏或物理内存换页。
  • IO问题:磁盘读写延迟高、吞吐低,或IO等待队列过长。
  • 网络问题:带宽饱和、连接数耗尽、TCP重传或DNS解析慢。
  • 锁竞争问题:线程因等待共享资源锁而阻塞,并发度形同虚设。

值得注意的是,问题之间会相互转化。例如内存不足导致频繁GC,GC线程抢占CPU又引发CPU飙升;磁盘IO慢导致线程阻塞,阻塞线程持有锁又引发锁竞争。因此分类不是孤立的,而是用于建立第一判断方向。

快速判断口诀:CPU看利用率与等待数,内存看GC与换页,IO看await与util,网络看重传与队列,锁看阻塞线程与持锁时间。

方法流程:五类问题的特征识别与定位路径

每一类问题都有其标志性的可观测指标。下表汇总了核心特征与首选排查工具,便于对照现场数据快速归类。

问题类型 典型症状 关键指标 首选工具
CPU 请求RT升高、CPU核满载 %user、%sys、负载均值、上下文切换次数 top / perf / jstack
内存 频繁Full GC、OOM、Swap占用高 堆使用率、GC频率、老年代增长、页交换 jstat / MAT / sar -r
IO 数据读写慢、日志刷盘卡顿 iowait%、await、svctm、util% iostat / iotop / strace
网络 接口超时、连接被拒、传输慢 重传率、RTT、连接队列溢出、带宽占用 netstat / tcpdump / ss
锁竞争 线程大量BLOCKED、CPU利用率不高但RT高 阻塞线程数、锁等待时间、持锁时长 jstack / arthas / JFR

定位流程建议遵循“先全局后局部,先资源后代码”的原则:

  1. 使用top、vmstat、sar观察系统级资源水位,确定瓶颈域。
  2. 针对瓶颈域深入:CPU问题抓取线程栈,内存问题分析堆转储,IO问题查看设备队列。
  3. 结合应用日志与监控曲线,确认问题发生的时间窗口与业务操作的对应关系。
  4. 必要时做隔离验证,例如摘除某台节点或关闭某项功能,观察指标是否恢复。

案例场景:一次典型的锁竞争误判为CPU问题

某订单服务在促销期间出现接口平均耗时从50ms飙升至2s,top显示CPU利用率仅35%,但vmstat中r(运行队列)并不高,cs(上下文切换)却高达每秒80万次。初步判断是CPU调度问题,但抓取jstack后发现大量线程处于BLOCKED状态,阻塞点集中在订单状态机的synchronized代码块上。

"http-nio-8080-exec-47" #67 daemon prio=5 os_prio=0 tid=0x... nid=0x... waiting for monitor entry
  java.lang.Thread.State: BLOCKED (on object monitor)
  at com.xxx.order.StateMachine.transit(StateMachine.java:87)
  - waiting to lock <0x00000000e1a2b3c0> (a com.xxx.order.OrderContext)
  at com.xxx.order.OrderService.updateStatus(OrderService.java:152)

进一步分析发现,单笔订单的整个状态流转过程都持锁,且包含一次远程库存查询(平均80ms)。持锁时间过长导致并发请求全部排队。修复方案是将锁粒度从“订单全流程”缩小到“状态字段更新”的临界区,并将远程调用移出锁块。优化后,相同流量下线程BLOCKED消失,RT回落至60ms。

该案例的启示是:CPU利用率不高不代表没有性能问题,线程阻塞同样会造成RT飙升。观察指标时,不能只看CPU,还要关注线程状态分布。

误区与复盘:分类排查中的常见陷阱

  • 误区一:CPU高就一定是计算密集。实际上,频繁GC、死循环、正则回溯、序列化消耗都会拉高CPU。应抓取线程栈确认是用户代码还是JVM内部线程消耗。
  • 误区二:内存大就不会OOM。如果存在对象泄漏,堆再大也会被慢慢填满。要关注老年代增长曲线,而非只盯着剩余空间。
  • 误区三:IO慢就换SSD。如果IO队列深度不高但延迟高,可能是应用写放大或文件系统配置问题;如果util高但await不高,说明设备已饱和,换硬件才有效。
  • 误区四:网络超时就是带宽不足。TCP重传、连接队列溢出、DNS解析慢、NAT会话表满都可能造成超时,需要结合抓包分析。
  • 误区五:锁竞争只与并发量有关。持锁时间才是关键。即使并发量不高,如果临界区包含慢操作(远程调用、磁盘IO),也会造成严重阻塞。
复盘要点:分类不是终点,而是起点。每类问题都要建立“指标特征→根因假设→验证手段”的闭环。尤其警惕复合型问题——例如内存泄漏导致GC频繁,GC线程抢占CPU,CPU升高又拖慢业务线程,最终表现为CPU和内存双双异常。此时应从最底层的资源瓶颈入手,逐步剥离表象。

掌握五类问题的特征与定位路径,相当于拥有了一张性能排查的“地图”。当新的性能告警出现时,先对照指标归类,再沿着对应路径深入,能有效避免在错误方向上浪费时间。下一章将针对CPU问题的排查方法做详细展开。

性能问题分类总览:五类核心瓶颈 系统资源池 CPU · 内存 · IO · 网络 · 锁 资源供需失衡 → 性能劣化 用户请求 高并发 / 大流量 业务逻辑 线程 / 事务 / 调用 CPU 瓶颈 高使用率 · 上下文切换 top / perf / jstack 内存瓶颈 GC频繁 · OOM · 换页 jstat / MAT / sar -r IO 瓶颈 await高 · 队列长 iostat / iotop 网络瓶颈 重传 · 连接耗尽 · RTT高 netstat / tcpdump 锁竞争瓶颈 BLOCKED · 持锁时间长 jstack / arthas / JFR 识别特征 → 归类问题 → 选择工具 → 定位根因 → 验证修复