hutool
hutool copied to clipboard
ReentrantCache的clean方法同样可能导致外部资源泄露
基本原理和https://github.com/chinabugotech/hutool/issues/3957 一样。 ReentrantCache类的clear()源码:
public void clear() {
lock.lock();
try {
cacheMap.clear();
} finally {
lock.unlock();
}
}
测试代码
@Test
public void cacheClearMethodTest() throws Exception {
Cache<String, Connection> connectionCache = CacheUtil.newLFUCache(3);
//设置监听器,触发回调释放资源
connectionCache.setListener(new ConnCacheListener());
Class.forName("com.mysql.jdbc.Driver");
Connection connection1 = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb", "root", "root");
Connection connection2 = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb", "root", "root");
connectionCache.put("key1",connection1);
connectionCache.put("key2",connection2);
connectionCache.clear();//这行代码导致缓存对象从cache中清除了,但是缓存对象关联的数据库连接永久泄露了。
}
class ConnCacheListener implements CacheListener<String,Connection>{
@Override
public void onRemove(String key, Connection conn) {
//监听器,触发回调释放资源
try {
if (!conn.isClosed()) {
System.out.println("数据库连接资源释放中:"+conn);
conn.close(); // 资源手动释放
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
运行截图:cacheMap.size()等于0,但是数据库连接资源泄露
bug原因: cacheMap直接使用的是LinkedHashMap的clear方法,如果cacheMap中存放的是数据库连接资源对象,那么可能导致资源泄露。
提交修复代码: commit
@Override
public void clear() {
lock.lock();
try {
// 获取所有键的副本
Set<Mutable<K>> keys = new HashSet<>(cacheMap.keySet());
for (Mutable<K> key : keys) {
CacheObj<K, V> co = removeWithoutLock(key.get());
if (co != null) {
onRemove(co.key, co.obj); // 触发资源释放
}
}
} finally {
lock.unlock();
}
}