
[toc]
缘由是,这个异常:c3p0 force_kill_acquires A ResourcePool cannot acquire a new resource
https://github.com/swaldman/c3p0/issues/117;2019年后,C3P0又更新了, 所以升级一波, 同时又希望可以看到连接中的堆栈信息
unreturnedConnectionTimeout
defines a limit (in seconds) to how long a Connection may remain checked out. If set to a nozero value, unreturned, checked-out Connections that exceed this limit will be summarily destroyed, and then replaced in the pool. Obviously, you must take care to set this parameter to a value large enough that all intended operations on checked out Connections have time to complete. You can use this parameter to merely workaround unreliable client apps that fail to close() Connections.Much better than working-around is fixing. If, in addition to setting
unreturnedConnectionTimeout
, you setdebugUnreturnedConnectionStackTraces
totrue
, then a stack trace will be captured each time a Connection is checked-out. Whenever an unreturned Connection times out, that stack trace will be printed, revealing where a Connection was checked out that was not checked in promptly.debugUnreturnedConnectionStackTraces
is intended to be used only for debugging, as capturing a stack trace can slow down Connection check-out.
unreturnedConnectionTimeout
大概意思就是一个连接可以保持多久,超出这个限制就会被销毁debugUnreturnedConnectionStackTraces
:大概意思就是每次checkout连接的时候都会捕捉堆栈信息, 当未退出的连接超时的时候,将打印堆栈跟踪, 显示相关代码位置C3P0PooledConnectionPool
构造函数中 传入了 上述两个参数
C3P0PooledConnectionPool(
......
int unreturnedConnectionTimeout, //seconds
boolean debugUnreturnedConnectionStackTraces,
final ResourcePoolFactory fact
......
){
....
fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
....
}
相关参数(代码太长 略)
public Object checkoutResource( long timeout )
throws TimeoutException, ResourcePoolException, InterruptedException
{
.....
if (debug_store_checkout_exceptions)
card.checkoutStackTraceException = new Exception("DEBUG STACK TRACE: Overdue resource check-out stack trace.");
}
......
}
其中的
this.debug_store_checkout_exceptions = (debug_store_checkout_exceptions && destroy_unreturned_resc_time > 0);
debugUnreturnedConnectionStackTraces
BasicResourcePool
时的destroy_overdue_resc_time参数, 也即来自配置的unreturnedConnectionTimeout
参数乘以1000由于本系统不适合设置unreturnedConnectionTimeout
参数, 因为我们的连接可能需要保持很久很久,
但是我们依然希望能捕捉到长时间保持连接的线程,因此可以在不新增参数的情况下,去掉这个debug_store_checkout_exceptions
判断, 直接在card中持有线程信息;
如官网所说:捕获堆栈会降低checkout的效率
debugUnreturnedConnectionStackTraces
is intended to be used only for debugging, as capturing a stack trace can slow down Connection check-out.
public C3P0PooledConnectionPoolManager getPoolManagerRead() throws SQLException;
public C3P0PooledConnectionPoolManager getPoolManagerRead() {
return poolManager;
}
// 以下代码为追加
public HashMap getManaged() {
return rp.getManaged();
}
public Exception getValueException(Object res) {
return rp.getValueException(res);
}
public long getCheckoutTime(Object res) {
return rp.getCheckoutTime(res);
}
public HashMap getManaged();
public Exception getValueException(Object resource);
public long getCheckoutTime(Object resource);
public HashMap getManaged() {
return cloneOfManaged();
}
public Exception getValueException(Object resource) {
return ((PunchCard) this.managed.get(resource)).checkoutStackTraceException;
}
public long getCheckoutTime(Object resource) {
return ((PunchCard) this.managed.get(resource)).checkout_time;
}
BasicResourcePool#checkoutResource中修改去掉debug_store_checkout_exceptions判断, 持有堆栈
// 去掉判断, 持有堆栈, 但是会降低checkout的效率
// if (debug_store_checkout_exceptions)
card.checkoutStackTraceException = new Exception(
"DEBUG STACK TRACE: Overdue resource check-out stack trace.");
StringBuilder sb = new StringBuilder();
for (Iterator ii = C3P0Registry.getPooledDataSources().iterator(); ii.hasNext();) {
PooledDataSource pds = (PooledDataSource) ii.next();
sb.append(pds.toString());
// sb.append("池中的链接数:" + pds.getNumConnections());
sb.append("池中的链接数:" + pds.getNumConnectionsDefaultUser());
// sb.append("池中的空闲链接数:" + pds.getNumIdleConnections());
sb.append("池中的空闲链接数:" + pds.getNumIdleConnectionsDefaultUser());
// sb.append("池中的被checkout链接数:" + pds.getNumBusyConnections());
sb.append("池中的被checkout链接数:" + pds.getNumBusyConnectionsDefaultUser());
C3P0PooledConnectionPool pool = null;
try {
pool = pds.getPoolManagerRead().getPool();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
int timeCost = 18 * 1000;
Set keySet = null;
try {
keySet = pool.getManaged().keySet();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (keySet != null && !keySet.isEmpty()) {
for (Object key : keySet) {
long checkoutTime = 0;
try {
checkoutTime = pool.getCheckoutTime(key);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
if (checkoutTime > 0) {
long sub = System.currentTimeMillis() - checkoutTime;
if (sub > timeCost) {
Exception valueException = pool.getValueException(key);
if (valueException == null) {
continue;
}
{
String mess = "cost more than " + timeCost + " : " + sub;
logger.error(mess, valueException);
sb.append("-----------------------------\n");
StringWriter sw = new StringWriter();
valueException.printStackTrace(new PrintWriter(sw, true));
sb.append(mess);
sb.append(sw.getBuffer().toString());
sb.append("-----------------------------\n");
}
}
}
}
}
}
sb.append("\n" + "unclosedPooledDataSources/getNumPoolsAllDataSources:" + C3P0Registry.getNumPooledDataSources()
+ "/" + C3P0Registry.getNumPoolsAllDataSources());
Runtime runtime = Runtime.getRuntime();
long max = runtime.maxMemory();
sb.append("最大内存(是通过启动JAVA虚拟机时使用参数-Xmx100m指定的)=" + (max / 1024 / 1024) + "M");
long total = runtime.totalMemory();
sb.append("已分配内存 =" + (total / 1024 / 1024) + "M");
long free = runtime.freeMemory();
sb.append("已分配内存中的剩余空间=" + (free / 1024 / 1024) + "M");
sb.append("最大可用内存 =" + ((max - total + free) / 1024 / 1024) + "M");
String string = sb.toString();
string = string.replaceAll("->", "\t");
string = string.replaceAll(",", "\n");
logger.warn(string);
return string;