背景
上午 10 点左右我被拉进一个群里,群里有个同事反馈某个接口延时变高希望我们处理下。我本人之前不负责这个服务,因为同事都 🐑 了,所以叫我来救火 🐶!(无从下手,一头雾水)
jvm gc time/count 监控
先看了下监控服务 jvm 果然不正常 ,gc 耗时 80 多秒了 🐶

接口流量监控
然后看一下接口流量上有啥变化,发现某个接口的调用量从前一天下午三点后开始增多,推测是这个是触发 gc 异常的条件
MAT 分析 dump 文件
不过光有推测还是不行的,需要拿出证据,可惜的是上图 jvm 异常的现场没有了(我另外一个同事使用了重启大法)。我只能在测试环境模拟复现这个场景,写个脚本大量调用这个接口,果然测试环境的 gc 也异常了,然后我 dump 了整个 jvm,再使用我的大杀器 MAT 分析。MAT 提示 org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler 类的某个对象占用 1.65g 的内存不能释放,这不就是 spring 对线程池的封装嘛,那有理由推测是线程池使用不当导致内存泄露的。那是谁使用了这个类呢?

谁使用了 ThreadPoolTaskScheduler
看了下这个类的源码,发现子类很多调用这个类的也很多,此时毫无头绪。无奈,一顿百度找到了一些灵感,spring 的@async 注解
是否使用了@async 注解
撸一下源码看一下这个接口是否使用了该注解,如下图果然使用了


@async 注解为啥会导致内存溢出
先分析下,ThreadPoolTaskScheduler 是个线程池,线程池导致内存溢出的可能就是线程池的队列是个无界队列。那我们来验证一下这个猜想。