线程池oom八成源于无界队列,如linkedblockingqueue默认容量为integer.max_value,任务积压不执行导致对象长期存活、堆内存持续增长;应改用有界队列并启用可监控的拒绝策略。

线程池引发的 OOM,八成和无界队列脱不了干系。不是线程太多,而是任务在队列里“躺平”不执行,越堆越多,把堆内存直接撑爆。
为什么无界队列是内存隐形杀手
所谓“无界”,比如 new LinkedBlockingQueue(),实际容量是 Integer.MAX_VALUE(约21亿)——逻辑上像有界,实践中就是无界。只要任务提交速度略高于处理速度,队列就持续膨胀,里面每个 Runnable、每个业务对象(比如一个 6KB 的 User 导入任务)都长期存活,GC 清不掉。
- 任务不执行,对象不释放,堆内存只增不减
- 线程池不会扩容(因为队列永远“没满”),maximumPoolSize 形同虚设
- 监控上看:活跃线程数稳定,但队列 size 持续上涨,内存使用曲线同步爬升
哪些写法最容易踩坑
这些看似常规的配置,上线后可能就是定时炸弹:
- Executors.newFixedThreadPool(n):底层用的就是无界 LinkedBlockingQueue
- Executors.newCachedThreadPool():SynchronousQueue 虽无缓冲,但最大线程数为 Integer.MAX_VALUE,高并发下线程数爆炸,触发 unable to create new native thread
- 手动构造 ThreadPoolExecutor 时漏写队列容量:new LinkedBlockingQueue() → 默认无界
怎么提前发现积压风险
别等 OOM 才行动。日常就要盯住两个核心指标,并打点到监控系统:
- executor.getActiveCount():反映真实并发压力。长期低于 corePoolSize?说明任务根本没被消费
- executor.getQueue().size():队列任务数。> 队列容量 70% 就该告警;持续 >90% 基本确认积压已发生
- 配合 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError,OOM 时自动生成 dump,便于事后验证
安全配置的关键动作
堵不如疏,关键在“可控排队”:
- 一律使用有界队列:ArrayBlockingQueue(推荐)或指定容量的 LinkedBlockingQueue(如 new LinkedBlockingQueue(200))
- 拒绝策略必须启用且可感知:AbortPolicy + 监控告警(失败即报警),或 CallerRunsPolicy(让调用方背锅,天然限流)
- 按业务类型选队列:
- CPU 密集型 → SynchronousQueue + corePoolSize = CPU 核心数 + 1
- IO 密集型 → 有界队列 + corePoolSize 可适当放大,但必须配拒绝策略










