首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >线程泄漏(一)-之线程池使用不当

线程泄漏(一)-之线程池使用不当

作者头像
用户12591013
发布2026-07-10 15:29:40
发布2026-07-10 15:29:40
30
举报

1.问题

线上经常有项目发生oom,而且oom是由操作系统抛出,而非jvm抛出。

2.排查过程

找了一个经常oom的项目实例,进行分析:

  • step1: 查看jvm参数配置
代码语言:javascript
复制
  • step2: 查看jvm监控
图片
图片

jvm参数配置并无太大问题,jvm监控中堆内存,元空间都不大,非堆也不大,但是jvm线程数不正常,达到了几千个,最高时达到16k,且线程数是稳步增长的

  • step3. 找动态线程池监控,未查到本项目信息
  • step4. 找到应用同学了解业务场景。

得知某个功能使用了线程池进行业务操作。观察代码初步判断,问题出在线程池使用不当。

3.问题分析模拟

3.1 模拟程序

针对上面排查,写了一个简化的模拟程序(仅仅模拟线程池使用,省略业务处理),验证判断。

代码语言:javascript
复制
@GetMapping("/testThread")
public String testThreadPool() {
    ExecutorService executor = Executors.newFixedThreadPool(100, ThreadFactoryBuilder.create().setNamePrefix("test-leak-").build());
    for (int i = 0; i < 1000; i++) {
        executor.submit(() -> {
           logger.info("test");
        });
    }
    return "success";
}
代码语言:javascript
复制

说明:这个逻辑写在controller中,get方法,每次执行时会创建一个线程池,用完并没有shutdown。

3.2 模拟步骤:

  • Postman 发送5次请求
图片
图片
  • 强制gc
代码语言:javascript
复制
  • Threaddump
代码语言:javascript
复制
  • 查看thread信息

从下图可以看见,叫test-leak的线程,一共有500个。

图片
图片

4.结论

业务代码在方法的执行过程中,new了一个线程池,并使用该线程池执行业务,在方法执行完毕后,并未执行shutDown方法,结束线程。这会导致内存泄漏,创建出来的线程,并不能被jvm回收掉。而业务方法的入口,是一个定时任务,所以,每执行一次都会导致线程数增加,所以我们能从监控中看到线程数不断稳步增长。

其它几个小知识

  • Q1. 方法执行完,线程池已经没有引用了,为啥不被jvm回收?

A:因为线程池中有活跃的线程,这个线程会有引用指向当前线程池对象,而thread本身时java gc 的root对象。这就导致了,垃圾收集器在计算可达对象时,这个线程池始终有引用指向它(可达到),所以线程池对象不会被回收,而里面的线程也不会被回收。

  • Q2. 普通的thread对象,执行完会被回收吗?

A:普通的thread对象,start以后开始执行,执行完以后自动就会销毁。但是线程池里的线程不会自动被销毁,只有shutdown以后才行。

5.建议

  1. 业务代码中不要在方法中new 线程池,建议直接使用动态线程池,以field的形式使用
  2. 线程池的coresize 不要太大,设置时考虑自己的cpu个数,理论上纯内存操作的话coresize <= cpu个数; io密集型,可以适当cpu个数的几倍来设置 如:2倍--8倍
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 鹏哥调优笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.问题
  • 2.排查过程
  • 3.问题分析模拟
    • 3.1 模拟程序
    • 3.2 模拟步骤:
  • 4.结论
  • 5.建议
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档