最近有个后台项目开发环境报OOM, 最后利用mat分析出具体原因是,在开发环境,开发人员为了方便,用jboss的热部署来部署更新完的war包,重新部署后,会reload spring的容器类, 但是有的类实例不能被卸载,导致有多个实例,之前存在的实例的定时任务一直在执行,最后导致OOM。 于是分析了具体不能被卸载的类,主要有以下2种情况:
- 存在定时任务没有被停止,造成有活跃的Thread,由于被根对象引用,造成不能被回收
- 实例被静态单例的类实例引用,如果要回收这个实例,必须回收Class对象,但是Class对象ClassLoader引用,而ClassLoader不能被回收的话,造成静态实例不能被回收。
以下用测试程序说明下:
定时任务没有被停止
修改之前
类对象
1 | @Service |
加载Spring Bean并reload
1 | public class Main { |
mat分析dump得到如下
查看被哪个根路径引用
修改之后
类对象
1 | @Service |
mat分析dump得到如下
静态类实例引用
修改之前
类对象
1 | @Service |
1 | public class StaticContainer { |
reload spring的类
1 | public class Main { |
mat分析dump得到如下
查看被哪个根路径引用
修改之后
类对象
1 | public class StaticContainer { |
1 | @Service |
mat分析dump得到如下
因此在开发时,尤其是中间件等框架,要注意:
- 对象销毁时需要停止定时任务, 以及设置相关变量的值
- 对引用外部实例的API,尤其注意要销毁清除