学习日报|线程池 OOM 案例与优化思路
一、典型场景:突发流量 + 配置不合理
线程池消费能力不足,任务队列无限堆积。
如:订单接口平时 QPS=100,突然飙升至 1000;配置 core=3, max=5, queue=1000 → 每秒堆积近千任务,短时间即溢出。
风险:若使用无界队列(如
LinkedBlockingQueue
默认),任务对象无限堆积,直接撑爆堆内存。
二、隐蔽场景:任务消费阻塞
QPS 不变,但任务执行时间突然变长(如调用第三方接口从 100ms → 10s)。
消费速率骤降,队列不断堆积,形成“温水煮青蛙式”的 OOM。
真实案例:短信推送线程池被第三方接口卡死,24 小时堆积百万任务,内存逐渐耗尽。
三、特殊场景:任务内存泄漏 + 队列堆积
任务对象执行完仍持有大对象(如 FileInputStream、Process)未释放。
队列堆积 + 内存泄漏叠加 → OOM 更快,且更难排查。
四、底层场景:框架/中间件的“隐式线程池”
如:Spring
@Async
默认线程池、MQ 消费者内部池、XXL-Job 定时任务线程池。若内部用无界队列 + 消费速度跟不上 → 任务同样无限堆积。
五、优化思路(核心对策)
有界队列 + 合理拒绝策略
明确指定队列容量,避免默认无界。
自定义拒绝策略:日志记录 + 限流提示 + 告警。
控制任务生产速度(流量限制)
接口层限流(Sentinel/Gateway)。
MQ 消费端限速(限制单批次拉取数量)。
任务执行加超时控制
外部调用必须设置 connect/read 超时。
用
Future.get(timeout)
控制任务执行时间,超时自动取消。
监控与预警
线程池:活跃线程数、队列长度、拒绝次数。
JVM:堆内存使用率。
指标暴露 + Grafana/Prometheus 告警,提前发现堆积。
学习总结
线程池 OOM 的根因是 任务生产速度 > 消费速度,且缺乏 流量控制/容量约束。
突发流量、消费阻塞、内存泄漏、框架隐式池 → 都会导致类似风险。
本质心法:
“任务要限产、队列要有限、任务要能超时、状态要可监控。”