上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

时间:2021-03-24 04:54:57

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

在发送量接近200w情况下,出现内存泄露问题了。

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

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

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

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

上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXJqaWNr/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

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

上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXJqaWNr/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

这个是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 com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

明白问题在mysql jdbc上面后。根本解决的方法应该查询mysql jdbc是否攻克了这个bug。

上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

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

上Mysql com.mysql.jdbc.StatementImpl$CancelTask内存泄漏问题和解决方法

讨论并协助谢国波感和锐利康。使故障得以圆满解决!