java连接access_记录一次Jedis连接池内存泄露

caedb15e95277beae4a044fffba3cbca.png

原本在排查一个项目的GC问题,借用另一个工程的堆dump来对比一下 ,结果发现了不为人知的陈年老bug。

本文关键词:Jedis连接池,对象池,Java,内存泄漏

bug看起来很简单,从Map中get对象和put对象的时候key不一样

private 

可是用MAT (Memory Analyzer)分析堆内存时发现堆中有12万个对象池

f1c22c930a07538b0382a04946b23084.png
12万个对象池

根据对象池中的_factory是redis.clients.jedis.JedisPool$JedisFactory分析得出这些pool是Jedis连接池。为什么会有这么多呢。

1f5ea885e45d3e2ae62fcd63c73308a0.png
Pool是JedisPool

根据可达性分析,mapJedisPool不再引用连接池的话应该会被GC回收了啊。带着这个疑问,我们利用MAT的Merge Shortest Paths to GC Roots功能,看下到底是谁揪着连接池不放。

ab54859a920ece28d2464cc871dd4e7c.png
GC Root

java.util.TaskQueue。没见过啊,不慌,从Jedis连接池看起吧。

从redis.clients.jedis.JedisPool的构造函数看起,JedisPool继承自org.apache.commons.pool.impl.GenericObjectPool,追溯到GenericObjectPool的构造函数(此处超链接为1.5版本的源码,2.0版本后package是org.apache.commons.pool2.impl了)调用了一个启动清理器的方法startEvicotr。为本文阅读方便,将部分代码粘贴过来。

public 

这个startEvictor的参数delay,如果小于0,是不会启动的,所以我们需要知道JedisPool的构造参数,Jedis构造参数中的org.apache.commons.pool.impl.GenericObjectPool.Config可以使用Jedis中的子类redis.clients.jedis.JedisPoolConfig,对于_timeBetweenEvictionRunsMillis的设置的30000ms,显然大于0。

那么EvictionTimer.schedule这个方法就会被调用,这个在静态变量java.util.Timer不存在时new一个Timer,将空闲连接回收任务放入Timer中。各位看官,坚持住,马上就找到找到根源了!

class 

java.util.Time里的第一个成员变量就是GC Root中的java.util.TaskQueue了!TaskQueue这个类就在Timer这个代码文件里。

至此,我们找到了内存泄露的根源,Jedis连接池持有一个org.apache.commons.pool.impl.GenericObjectPool.Evictor对象,这个对象被EvictionTimer中的静态成员变量持有,导致连接池永远不会被垃圾回收器标记为可回收的垃圾(可能是干垃圾),而这个连接池在程序代码中也不会再有引用,也就成了一个野指针,造成了内存泄露。

可能有朋友好奇为什么没有发生OutOfMemoryError,因为JVM内存不小,暂时还够用,并且项目迭代上线较快,相当于经常重启。

那么解决的办法是什么呢,首先改掉第一段代码中对Map进行put和get时key不一致的bug;在并发条件下,也可能创建多个连接池,在判断containsKey后,对Map加锁,二次判断;

在Jedis连接池不再使用时,需要调用destroy方法销毁线程池,使用其他GenericObjectPool时同样需要在不再使用时调用close方法。

封面图片:https://westergaard.eu/wp-content/uploads/2017/07/edorasware-illustration-memory-leak-horizontal.jpg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值