前言
本篇其实是承接前面两篇的,都是讲定位线上的c3p0数据库连接池,发生连接泄露的问题。
第二篇讲到,可以配置两个参数,来找出是哪里的代码借了连接后没有归还。但是,在我这边的情况是,对于没有归还的连接,借用者的堆栈确实是打印到日志了,但是我在本地模拟的时候,发现其实这些场景是有归还连接的,所以,我开始怀疑不是代码问题。
不是业务代码问题,能是啥问题呢?我们先来看看连接是怎么归还到连接池的。
连接的实际类型
我在本地debug了下,发现获取连接时,代码如下:
com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource#getConnection()
public Connection getConnection() throws SQLException
{
// javax.sql.PooledConnection,实际类型为com.mchange.v2.c3p0.impl.NewPooledConnection
PooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();
return pc.getConnection();
}
说实话,之前都没注意到jdbc api里还有javax.sql.PooledConnection
这个类,这里,就是首先从c3p0连接池获取了一个com.mchange.v2.c3p0.impl.NewPooledConnection
对象,然后转换为javax.sql.PooledConnection
。
然后,调用javax.sql.PooledConnection#getConnection
,会返回给实际类型为com.mchange.v2.c3p0.impl.NewProxyConnection
的对象。
com.mchange.v2.c3p0.impl.NewPooledConnection#getConnection
public synchronized Connection getConnection() throws SQLException
{
if ( exposedProxy == null )
{
exposedProxy = new NewProxyConnection( physicalConnection, this );
}
return exposedProxy;
}
在该类中,主要包含如下几个字段:
inner:实际的底层连接,如我这里,其类型为oracle.jdbc.driver.T4CConnection
parentPooledConnection:javax.sql.PooledConnection类型的池化连接
cel:类型为ConnectionEventListener,就是一个监听器
connection.close方法逻辑
com.mchange.v2.c3p0.impl.NewProxyConnection
public synchronized void close() throws SQLException {
// 0
if (!this.isDetached()) {
// 1
NewPooledConnection npc = this.parentPooledConnection;
this.detach();
// 2
npc.markClosedProxyConnection(this, this.txn_known_resolved);
this.inner = null;
}
}
0处,检查该对象是否已经和底层的池化连接解绑:
boolean isDetached() {
return this.parentPooledConnection == null;
}
1处,通过parentPooledCon