一次线上内存泄漏分析

一次线上内存泄漏分析


现象

线上某个服务A在运行一段时间后,内存会逐步涨高,而且不会下降,最终导致OOM。

分析

  1. 一开始只知道OOM了,无日志输出,无dump文件,不确定是什么原因导致的,于是在程序启动时,增加了参数配置,在程序OOM时,生成dump文件到/dump/ 目录:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump/

  1. 程序正常运行一段时间后,无意外的再次发生OOM,在/dump/目录把xx.hprof文件下载到本地,借用eclipse的MAT
    插件打开。
  2. 这个插件有个好处,在分析dump文件后,会给出可能有问题的对象。

在这里插入图片描述

  1. 顺着提示,分析对象,我们可以看到大量的dubbo泛化调用产生的链接数量有到32万+。

在这里插入图片描述

在这里插入图片描述

  1. 重新捋了一遍泛化调用的代码:
  • 这里有一个很关键点,每一次泛化调用,都会重新new一个ReferenceConfig,这个对象包含着服务提供方链接及注册中心地址,而从dump文件分析来看,这个对象估计在这个方法执行完成之后,没有回收,导致内存一直增长。

在这里插入图片描述

  • 这里的ReferenceConfig是在方法内构建的,讲道理,在方法执行完成后会被回收释放,但现在看来是没有被回收,那就是在哪里被引用了,导致迟迟没法回收?(待继续深入研究)

解决

既然知道原因,目前在客户端加入了缓存,把referenceconfig对象用一个serviceMap缓存下来,只要在获取为null时才去创建。

private val application: ApplicationConfig = run {
        val zookeeperHostName = ApolloConfigReaderUtils.getString("zookeeper.hostName")
        val zookeeperHostPort = ApolloConfigReaderUtils.getString("zookeeper.port")
        val zookeeperFilePath = ApolloConfigReaderUtils.getString("dubbo.registry.file.path")

        val application = ApplicationConfig()
        application.name = "****"
        val registry = RegistryConfig()
        registry.address = """zookeeper://$zookeeperHostName:$zookeeperHostPort"""
        registry.file = zookeeperFilePath
        application.registry = registry
        application
    }

    private val serviceMap = mutableMapOf<String, GenericService>()

    private fun getGenericService(serviceName: String) : GenericService {
        if (serviceMap.get(serviceName) == null) {
            synchronized(this) {
                val reference = ReferenceConfig<GenericService>()
                // 弱类型接口名
                reference.setInterface(serviceName)
                // System.setProperty("dubbo_group", "dev1")
                reference.group = System.getenv("dubbo_group")
                reference.isCheck = false
                // 声明为泛化接口
                reference.isGeneric = true
                reference.application = application
                // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
                serviceMap.put(serviceName, reference.get())
            }
        }
        return serviceMap.get(serviceName)!!
    }

后记

目前团队需要一名4年工作经验的朋友加入,坐标广州,如果有道友有兴趣的话,欢迎私信了解!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

legendaryhaha

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值