spring-boot-devtools引起的内存性能问题(官方至今未解决,如有此依赖,请看完)

背景

在服务器上部署用户中心后发现其内存占用比其他Springboot应用都要高,且启动后内存占用自动升高,存在异常
在这里插入图片描述

排查

用arthas分析一下jvm内存情况,发现分配的内存远小于已使用的内存,老年代空间使用也不多,初步推迟是新生代空间占用过多导致内存居高不下
在这里插入图片描述
既然如此,导出堆内存快照分析一下内存里都有哪些对象占用了内存

在这里插入图片描述

堆内存快照导入jprofile后发现,被GCRoot引用的内存只有104M(总的堆内存占用有800多M),固存在大量临时内存,再看看哪些地方引用了byte[]

在这里插入图片描述
未发现明显异常,均是第三方类库的引用

为什么会存在大量byte[]呢?什么情况下会产生大量的byte[]数据呢?网络IO?文件IO?
网络IO就是mysql和redis会有,文件IO是否有也未知
尝试启动项目后断开VPN,这样就不会有网络IO了吧
在这里插入图片描述
结果内存情况依旧如此,网络IO—pass

本地跑下项目用jprofiler监控一下看看
在这里插入图片描述
在这里插入图片描述
观察到byte[]占用的内存会不断增多,但总内达到800多M时会触发young gc(差不多两分钟一次)
再看看CPU视图下的调用树
在这里插入图片描述
就是Springboot启动和一些业务线程的调用。

但是,devtools?不是用来热部署的么,正好项目中有引入spring-boot-devtools的依赖,难道是热部署一直扫描jar包外的文件的IO操作导致的byte[]内存升高?

带着猜测去除依赖验证一下
在这里插入图片描述
在这里插入图片描述
果然如此。。

解决办法

三种方法:

  • 取消依赖
  • 关闭热部署spring.devtools.restart.enabled: false
  • 调大文件轮训间隔时间
spring.devtools.restart.pollInterval: 1s(轮询类路径更改之间等待的时间量,默认1s。实测调整为10s后,20分钟才会young gc)
spring.devtools.restart.quietPeriod: 400ms(在触发重启之前,在不更改任何类路径的情况下所需的安静时间,默认400ms)

刨根问底

那么问题就来了,是什么原因导致的内存泄露呢?

通过阅读devtools的源码,发现有一个File Watcher的线程,轮训的去扫描指定目录下文件并和上一次扫描的文件比较,来判断文件是否有变化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
怀疑是在这个线程中,scan方法不断获取新的文件内容(current = getCurrentSnapshots();),导致新生代内存中files不断增加,内存溢出

尝试主动停止这个线程试试
在这里插入图片描述
重启项目,内存不在飙升
在这里插入图片描述
看看最新版本的devtools有没有优化这个问题,
FileSystemWatcher.java

很遗憾,这块代码逻辑没变。。。

在issue中发现有同样性能问题:https://github.com/spring-projects/spring-boot/issues/9882

最终决定使用基于NIO的文件监视器来解决这个问题,然而目前并未得到解决

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值