com.mysql.jdbc.nonregisteringdriver_解决com.mysql.jdbc.NonRegisteringDriver的内存泄漏

因为游戏的服务端是用Java编写的,大量使用了缓存和数据异步写入机制,但在运行的过程中要大量记录运营日志,所以数据库的读写虽然可能不像web服务器那样频繁,但数据库的开销其实还是相对可观的。上线运营一段时间后,服务器越来越卡,而且还出现Out Of Memory的情况。

分析一:使用jmap命令观察到Jvm的GC已经到了很危险的情况,JVM的新生代和老年代都几乎已经消耗完毕了,如下图。

8b91a973c01858bde7d01c5b356b7004.png

分析二:继续查看JVM 的GC日志,下面摘抄几条:

2014-05-16T13:57:23.958+0800:592015.629: [Full GC 1948862K->1943518K(2024256K), 0.8818560 secs]

2014-05-16T13:58:10.682+0800: 592062.352:[Full GC 1948862K->1943638K(2024256K), 1.0139730 secs]

2014-05-16T13:59:18.427+0800: 592130.097:[Full GC 1948862K->1943686K(2024256K), 0.9705680 secs]

FULL GC出现的情况越来越频繁, 到了最后隔几秒就要Full GC一次,而且所需要的时间越来越长,Minor GC 已经几乎不出现了,与上图中的情况完全吻合。

分析三:使用jmap-dump:format=b,file=logic_heap.hprof  pid这个命令,dump出内存的映像状态文件,再使用MemoryAnalyzer这个工具分析。发现有大量的com.mysql.jdbc.NonRegisteringDriver这个对象产生泄漏。这个类是MySQL的connector/J官方驱动,按道理是不用出大问题的,我一开始也是从来没怀疑过,而且也不想去碰里面的代码。于是一直分析mybatis的代码,再分析自己封装的dao层。到了最后,实在没办法了,只能把connector/J的源代码打开,发现了里面有一个很重要的变量

ConcurrentHashMapconnectionPhantomRefs

这个map保存了所有连接的实例的虚引用(PhantomReference),然后由AbandonedConnectionCleanupThread不断释放,但最关键的是,似乎PhantomReference的特性是只有当主动调用System.gc()这个方法时才会主动释放(可能我说得不对,但据我观察的情况确定是这样)。而由于之前从一些jvm的调优文章里看到建议不要主动调用System.gc(),因此我整个项目里都从来没主动调用过System.gc()。

4cca8d90967eb6a55e4dca9797f5c34d.png

最后,解决方法很简单,只要自己写一个定时器,隔一段时间执行一下,就可以源源不断地清理这些PhantomReference

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值