一次FullGC问题分析

        测试环境某个接口没响应,查看系统日志提示java.lang.OutOfMemoryError: Java heap space

居然OOM,看下GC日志,发现每秒执行一次FullGC。

由于设置了-XX:+HeapDumpOnOutOfMemoryError 参数,所以能直接拿到堆文件。使用MAT工具看了下,发现创建了大量的 AnnotationConfigApplicationContext 对象,且beanDefinitionMap都是些配置类。

看起来是开启了Devtools的热部署,Restarter对象持有了大量的AnnotationConfigApplicationContext 对象导致的,那为何会产生大量的对象呢?我试图在系统日志中寻找一些蛛丝马迹,发现有大量报错日志

报错Cause:

根据调用轨迹,报错原因是执行SeataDataSourceBeanPostProcessor的proxyDataSource方法出错

如果seata设置自动代理数据源,那么bean的初始化过程会执行该处理器

为何创建代理对象会报错?根据报错原因在网上查了下资料,发现这篇文章使用 Cglib ,Javassist 实现多重代理 解决:Caused by: java.lang.ClassFormatError: Duplicate method name...-CSDN博客

那第一次代理对象是什么时候创建的呢?带着疑问本地调试了下,原来是在系统启动过程中,执行BeanPostProcessor给数据源创建代理对象。

bean的初始化阶段完成后会把该代理放进IOC容器中。

那第二次代理对象又是什么时候创建的呢?分析报错日志,发现是nacos更新配置中心内容过程中触发的第二次代理对象创建,因创建失败导致报错。为了分析nacos更新配置过程中都做了什么,进行源码阅读。

ClientWorker会不断轮询服务端看是否有配置更新

当发现有更新内容就会发布一个RefreshEvent事件

触发RefreshEventListener监听器执行Bean刷新

继续跟踪刷新过程,发现会执行一个addConfigFilesToEnvironment方法,该方法内会重新执行SpringApplication#run( args )方法。

run方法的执行逻辑就是我们熟悉的IOC容器创建过程了,由于设置的WebApplicationType是NONE类型,所以创建了默认的AnnotationConfigApplicationContext对象

执行完prepareContext方法会发布一个ApplicationPreparedEvent事件

由于项目中引入了DevTools热部署工具,所以触发了RestartApplicationListener监听器

最后把applicationContext添加到Restarter#rootContexts列表中,而这个applicationContext的类型就是前面创建的默认类型AnnotationConfigApplicationContext。

到此,弄明白了AnnotationConfigApplicationContext是如何被Restarter引用的,但还没触发报错,继续跟踪。

执行完addConfigFilesToEnvironment()方法后,会获取要变更的属性项,然后发布一个EnvironmentChangeEvent事件。

该事件会被ConfigurationPropertiesRebinder监听,执行rebind()方法,rebind方法会把有@ConfigurationProperties注解的配置类重新初始化一遍,由于项目使用的是默认数据源,且有@ConfigurationProperties注解所以dataSource需要重新初始化

执行初始化之前先从容器中获取对应的Bean,这个Bean就是SeataDataSourceBeanPostProcessor第一次给DataSource创建的代理对象。

然后就是执行标准的bean初始化流程了

由于开了自动代理数据源,所以再次执行SeataDataSourceBeanPostProcessor。

注意: !(bean instanceof DataSourceProxy) 判断条件为true,所以再次执行创建代理对象,最后触发报错(不能给CGLIB对象创建代理对象,前文已有解释链接)。

异常被com.alibaba.nacos.client.config.impl.ClientWorker.LongPollingRunnable线程内部捕捉到,延迟2000ms后再次执行上述流程。

所以产生大量AnnotationConfigApplicationContext对象的原因就是:

创建对象 --> 放进Restarter --> 执行SeataDataSourceBeanPostProcessor --> 捕捉报错再次执行

Restarter持有AnnotationConfigApplicationContext对象引用,所以GC无法对这些对象进行垃圾回收,直到堆空间耗尽抛出OOM异常。

解决方案:

1:禁用devtools热部署

2:seata禁用自动代理数据源,改为手动声明

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值