Executors.newFixedThreadPool() 易引发内存溢出,根本原因是其默认使用的无界 LinkedBlockingQueue(容量为 Integer.MAX_VALUE),任务积压导致堆内存持续增长而触发 OOM。

Java 中 Executors.newFixedThreadPool() 容易引发内存溢出,根本原因不是线程数量固定,而是它背后那个“无限吃内存”的无界任务队列。
LinkedBlockingQueue 默认是无界的
该方法底层实际创建的是:
new ThreadPoolExecutor(n, n, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())- 而
LinkedBlockingQueue()的无参构造函数,会把容量设为Integer.MAX_VALUE(约 21 亿) - 这在逻辑上等同于“没有上限”,任务提交快、执行慢时,队列就持续堆积
每个待执行任务都占用堆内存
Runnable 对象本身不重,但它往往携带大量上下文数据:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- HTTP 请求体、JSON 参数、数据库查询条件、DTO 实例、日志上下文等
- 一个中等大小的请求对象可能占几百 KB,积压几千个就轻松突破 1GB 堆内存
- OOM 不是突然发生的,而是队列缓慢膨胀、GC 无力回收导致的渐进式崩溃
拒绝策略形同虚设
无界队列下,maximumPoolSize 和拒绝策略完全失效:
立即学习“Java免费学习笔记(深入)”;
- 因为队列永远“不满”,新任务永远能塞进去,never trigger rejection
- 线程数始终卡在
corePoolSize,系统看似“稳定”,实则隐患深埋 - 等到监控发现异常,往往已接近或触发
java.lang.OutOfMemoryError: Java heap space
其他同类线程池也有同样问题
不只是 newFixedThreadPool,以下方式同样危险:
-
newSingleThreadExecutor():也是无界队列,单线程处理 + 无限排队 = 更容易堵死 -
newCachedThreadPool():最大线程数为Integer.MAX_VALUE,突发流量下线程疯涨,可能触发unable to create new native thread

















