关于Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题及解决办法

近来在负责公司短信网关的维护及建设,随着公司业务发展对短信依赖越来越严重了,短信每天发送量也比以前每天40多w发送量暴增到每天达到200w发送量。因为是采用Java做发送底层,压力递增情况下不可避免的面对内存问题。在发送量接近200w情况下,出现内存泄露问题了。

经过对系统运行检查发现:

        1)每次重启系统3-4个小时后,均发现一点不稳定;

        2)在3-4个小时后,出现out of memory的错误:java.lang.OutOfMemoryError: GC overhead limit exceeded

发现这个问题后,直接通过JMS获取监控日志,发现系统的内存回收存在异常,gc压力非常大而且存在明显内存积压。




然后直接把系统的内存down下来分析,发现的确存在内存积压情况:


这个是mysql的一个定时任务的,这个定时任务主要作用是在用于做查询超时的。简单举个例子,系统在执行一个sql查询情况下,jdbc会给你一个超时时间。为了保证超时时间后,可以关闭statement,会打开一个保护关闭的定时任务。如果超时情况下,sql还没响应执行,cancel task就会执行关闭任务。因为c3p0的默认设置的超时时间为25s(<setting name="defaultStatementTimeout" value="25000" />),意味这个25s内,在执行大量sql情况下,cancel task积压到了一定程度,就会造成系统不稳定。(最后发现这个并不是根本原因,只是表象)

但是系统本身就有通过mysql做发送队列的,本身对mysql操作非常多,如果只是对代码层面下优化基本杯水车薪。

在时间紧迫的情况下,短时间内稳定业务才是最重要的任务。被逼根据以上现状采用了临时方案解决。


临时方案

        通过以上判断,基本可以判定cancel task在某组线程执行应该会形成一个峰值,撑爆了JVM的堆。但是现在无法在停止业务运行做过多调试,所以当机立断,对JVM的内存扩大一倍以上,希望系统可以跨过一波内存峰值。结果把JVM的内存调整为故障时候的2倍时候,系统的内存又恢复到正常运作,不过cancel task最高值会占用到内存2G以上。虽然也是会回收,但是一直扩内存不是很好的办法。


解决方案

系统采用的mysql jdbc 5.1.6的版本,立刻反编译mysql代码,发现以下问题。因为cancel task的timer在connection中静态存放,意味statement如果正常查询出结构,业务无法把cancel task内存回收才是故障根本原因。


明确问题在mysql jdbc上面后,根本解决办法应该查询mysql jdbc是否解决了这个bug。

在5.1.11版本中找到这个bug的修复,更新后,内存泄漏故障得到解决。http://dev.mysql.com/doc/relnotes/connector-j/en/news-5-1-11.html


感谢国波与锐康的讨论及协助,使故障得以完满解决!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。