验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

ExecutorService会内存泄漏吗

阅读:499 来源:乙速云 作者:代码code

ExecutorService会内存泄漏吗

简短结论:
ExecutorService 本身不会无缘无故内存泄漏,但使用不当非常容易造成内存泄漏或类同问题。是否泄漏,取决于线程池的生命周期、任务引用、队列积压、ThreadLocal 使用等。

下面分层说明。


一、ExecutorService 什么时候“不会”内存泄漏

如果你满足以下条件,基本是安全的:

✅ 线程池 有界(核心线程数、最大线程数设置合理)
✅ 使用 shutdown() / shutdownNow() 正确关闭
✅ 任务 不持有外部大对象、Context、ClassLoader 引用
✅ 队列 不会无限堆积任务
✅ 没有滥用 ThreadLocal

例如:

ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> System.out.println("task"));
executor.shutdown();

✅ 这是安全的


二、ExecutorService 常见的“内存泄漏”场景(重点)

1️⃣ 线程池未关闭(最常见)

ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(task);
// 忘了 shutdown

后果:

  • 线程 一直存活
  • 线程栈、Thread 对象、任务引用 无法被 GC
  • 应用长期运行 → 内存占用持续增长

✅ 解决:

executor.shutdown();
// 或
executor.awaitTermination(30, TimeUnit.SECONDS);

2️⃣ 任务队列无限堆积(伪内存泄漏)

ExecutorService executor = Executors.newFixedThreadPool(1);
// 生产速度 >> 消费速度

问题:

  • LinkedBlockingQueue 默认 Integer.MAX_VALUE
  • 任务对象不断堆积
  • 内存暴涨

✅ 解决:

  • 使用 有界队列
  • 自定义 ThreadPoolExecutor
new ThreadPoolExecutor(
    4, 8,
    60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000),
    new ThreadPoolExecutor.AbortPolicy()
);

3️⃣ 任务持有大对象 / 外部引用

class Task implements Runnable {
    private BigObject bigObject;
}

如果:

  • 任务未执行完
  • 或任务被反复提交
  • 或线程池长期存活

➡ 大对象 无法释放

✅ 解决:

  • 任务中 不要长期持有大对象
  • 使用局部变量
  • 执行完及时解除引用

4️⃣ ThreadLocal 导致的内存泄漏(非常隐蔽)

ThreadLocal tl = new ThreadLocal<>();
tl.set(bigObject);

问题:

  • 线程池中的线程 是复用的
  • ThreadLocal 中对象 不会被自动清理
  • 线程不死 → 对象不死

✅ 正确做法:

try {
    tl.set(obj);
    // do work
} finally {
    tl.remove();
}

5️⃣ 非守护线程阻止 JVM 退出(看起来像泄漏)

Executors.newFixedThreadPool(1);
  • 默认线程是 非守护线程
  • 即使 main 结束,JVM 也不会退出

✅ 使用自定义 ThreadFactory:

new ThreadFactory() {
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

三、ExecutorService 会不会“自己”泄漏?

情况 结论
JDK 标准实现 ❌ 不会
正确使用 ❌ 不会
忘记 shutdown ✅ 会
队列无限 ✅ 会
ThreadLocal 滥用 ✅ 会
任务引用外部对象 ✅ 会

四、如何排查 ExecutorService 相关内存问题

1️⃣ 看线程数

jstack pid | grep "pool-" | wc -l

2️⃣ 看堆中对象

jmap -histo pid | head

3️⃣ 看队列大小(自定义线程池)

((ThreadPoolExecutor) executor).getQueue().size();

五、一句话总结

ExecutorService 本身不会内存泄漏,但它是“放大器”
一旦你管理不好生命周期、任务、队列或 ThreadLocal,它就会把问题放大成内存泄漏。

如果你愿意,可以把你具体的线程池代码贴出来,我可以帮你判断是否存在真实泄漏风险。

分享到:
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
相关文章
{{ v.title }}
{{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
你可能感兴趣
推荐阅读 更多>