一个让人困惑的现象

用户反馈页面卡死,接口超时。你打开监控:CPU 30%,内存正常,Grafana 一切绿色。

从资源角度看,系统完全健康。但它就是不能用。

CPU 低,只能说明系统没在算,不代表系统没在等。


三种常见根因

1. 线程池 I/O 阻塞

Tomcat 的 worker 线程在等待 I/O 操作(数据库查询、外部 API 调用),新请求进来没有线程处理,只能排队。

线程状态:大量 WAITING / TIMED_WAITING,CPU 自然不高。

2. 连接池耗尽

数据库 CPU 40%,看起来正常。但连接池已满(50/50),应用拿不到连接,请求全部阻塞。

这个问题在只看 CPU 和 QPS 的看板里完全看不出来。

3. 慢接口拖垮吞吐量

一个接口从 50ms 变成 300ms,线程池开始积压,P95/P99 延迟飙升,整体吞吐量下降——但 CPU 依然很低。


诊断方法

查线程状态

jstack [PID] | grep -c "WAITING\|BLOCKED"

查线程池饱和度

// 检查活跃线程数 vs 最大线程数
executor.getActiveCount() vs executor.getMaximumPoolSize()

查 GC 情况

jstat -gcutil [PID] 1000 10

频繁 Young GC + Stop-The-World 暂停,会导致请求延迟抖动。

查堆对象

jmap -histo [PID] | head -20

找出占用内存最多的对象类型,判断是否有内存泄漏。


传统监控的盲区

监控指标能发现的问题发现不了的问题
CPU 使用率计算密集型问题I/O 等待、线程阻塞
内存使用率内存不足内存泄漏早期、GC 压力
数据库 QPS查询量异常连接池耗尽、慢查询积压
接口成功率明显错误响应时间退化、超时积压

结论

成熟的运维团队,在系统「变慢」的早期阶段就能介入,而不是等到「崩溃」才响应。

这需要监控 JVM 内部状态,而不只是基础资源指标:线程池饱和度、连接池可用数、P95/P99 延迟、GC 频率——这些才是真正反映系统健康的指标。