BasicDataSource属性PoolPreparedStatements引发的OOM

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ywlmsm1224811/article/details/91047585

最近项目上遇到了OOM问题,如下图:

一、背景:项目上需要同步大量的老数据到新建的表中,持续比较长的时间使用common-dbcp连接池执行过程中出现了上述OOM问题,导致FULLGC一直卡死,CPU使用率居高不下

二、排查过程:

1、先查看应用进程号: ps -ef | grep 应用名 ,也就是 pid

2、查看pid垃圾回收情况: jstat -gc pid 5000(时间间隔)

3、dump jvm二进制的内存详细使用情况:  jmap -dump:format=b,file=/home/test/oom.hprof pid

4、使用 Memory Analyzer (MAT)来分析,简称MAT,分析内存泄露对象

注意: 其中 pid 是指程序进程号

通过MAT分析,得出结论:主要是由于大量的JDBC42PreparedStatement对象没有被回收引起的,即 openStatements 中引用的对象

三、分析原因

通过源码我们得知,清除 openStatements 中 JDBC42PreparedStatement对象需要两个条件:

1、connection close会清除,源码如下:

2、statement close,也会清除 openStatements 中 JDBC42PreparedStatement 对象,源码如下:

而我们知道,common-dbcp连接池不会直接关闭 connection 和 statement ,而是直接将 connection 和 statement 返回给连接池中,源码如下:

四、为什么设置了 PoolPreparedStatements 为 true时会引发这个问题

我们知道 PoolPreparedStatements 默认为 false,源码:

当设置为 true时,将会创建 GenericKeyedObjectPoolFactory ,从而创建出 PoolingConnection(common-dbcp中的类),如果是 false 时,将会创建 JDBC4Connection (mysql-connector中的类),源码如下:

当执行完sql语句调用 Statement 的 close 方法时, PoolPreparedStatements 为false时,调用的是 JDBC42PreparedStatement 的close方法, PoolPreparedStatements 为 true 时,调用的是 PoolablePreparedStatement 的 close 方法,下面结合源码和debug方法来分别说明:

1、PoolPreparedStatements 为false时,调用的是 JDBC42PreparedStatement 的close方法过程如下:

结论:上述过程最后会清除 OpenStatements

2、PoolPreparedStatements 为 true 时,调用的是 PoolablePreparedStatement 的 close 方法过程如下:

结论:上述过程只是将 connection 返回给连接池(pool)中而已,因此不会清除 OpenStatements

五、多思考一下,PoolPreparedStatements 设置为 true 时,有没有在什么情况下连接池也会清除 OpenStatements 的

答案是有的,当数据库连接池中的 connection 数量大于等于pool中允许的空闲的最大数量就会关闭 connection,从而清除 OpenStatements,源码如下:

猜你喜欢

转载自blog.csdn.net/ywlmsm1224811/article/details/91047585
OOM