现象
线上某个服务A在运行一段时间后,内存会逐步涨高,而且不会下降,最终导致OOM。
分析
- 一开始只知道OOM了,无日志输出,无dump文件,不确定是什么原因导致的,于是在程序启动时,增加了参数配置,在程序OOM时,生成dump文件到/dump/ 目录:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump/
- 程序正常运行一段时间后,无意外的再次发生OOM,在/dump/目录把xx.hprof文件下载到本地,借用eclipse的MAT
插件打开。 - 这个插件有个好处,在分析dump文件后,会给出可能有问题的对象。
- 顺着提示,分析对象,我们可以看到大量的dubbo泛化调用产生的链接数量有到32万+。
- 重新捋了一遍泛化调用的代码:
- 这里有一个很关键点,每一次泛化调用,都会重新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年工作经验的朋友加入,坐标广州,如果有道友有兴趣的话,欢迎私信了解!!