Spring Boot线程无法停止疑似内存泄漏 CompletableFuture多线程任务排查
问题根因
- 线程池未正确关闭:你在遍历
departmentList的循环内部,每次循环都通过Executors.newFixedThreadPool(20)新建一个独立的线程池,且处理完当前部门的任务后从未主动关闭该线程池。newFixedThreadPool创建的线程池默认核心线程数为20,且核心线程不会超时回收,所有空闲核心线程会一直阻塞在任务队列的take()方法等待新任务,和你日志中栈追踪的逻辑完全对应。当应用停止时,这些没有被关闭的线程池的存活线程会被Tomcat检测到,抛出内存泄漏警告。 - 线程池创建逻辑不合理:循环内反复创建线程池本身属于错误实践,反复创建销毁线程池会带来额外的性能开销,也提升了资源泄漏的概率。
修复建议
- 全局复用线程池:不要在循环内创建线程池,建议直接使用SpringBoot提供的
ThreadPoolTaskExecutor,在配置类中统一初始化线程池参数后全局注入使用,Spring会自动注册shutdown钩子,在应用停止时主动关闭线程池回收线程。 - 手动创建线程池必须主动关闭:如果需要手动创建线程池,在单次任务处理完成后必须调用
shutdown()方法主动触发线程池关闭,同时可以搭配awaitTermination()方法等待所有任务执行完成后再继续后续逻辑,参考代码:
executor.shutdown(); try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); }
- 优化线程池参数:可以给线程池设置
allowCoreThreadTimeOut(true),允许核心线程在空闲一段时间后自动销毁,避免空闲线程长期驻留。 - 调整
futureList作用域:将futureList的定义移到部门循环内部,每次处理完一个部门的任务后,该列表可以被正常GC,避免额外内存占用。
内容的提问来源于stack exchange,提问作者linuxNoob
相关产品推荐
相关产品推荐

