From 7739f8b015de211ed8df8a7cc5255f18cf329c4d Mon Sep 17 00:00:00 2001 From: yanzhongxin <747328867@qq.com> Date: Wed, 4 Jun 2025 15:03:11 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8DputWithoutLock=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E5=A4=96=E9=83=A8=E8=B5=84=E6=BA=90=E6=B3=84?= =?UTF-8?q?=E9=9C=B2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/hutool/cache/impl/AbstractCache.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java index 3465e9f00..3703478f0 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java @@ -89,7 +89,11 @@ public abstract class AbstractCache implements Cache { // issue#3618 对于替换的键值对,不做满队列检查和清除 if (cacheMap.containsKey(mKey)) { - // 存在相同key,覆盖之 + CacheObj oldObj = cacheMap.get(mKey); + if (oldObj != null) { + onRemove(oldObj.key, oldObj.obj); + cacheMap.remove(mKey); + } cacheMap.put(mKey, co); } else { if (isFull()) { From 59312647063d57a637235eb7326ba4289b8c6aba Mon Sep 17 00:00:00 2001 From: yanzhongxin <747328867@qq.com> Date: Fri, 6 Jun 2025 15:05:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8DReentrantCache=E7=9A=84cl?= =?UTF-8?q?ear=E5=AD=98=E5=9C=A8=E7=9A=84=E5=A4=96=E9=83=A8=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E6=B3=84=E9=9C=B2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/cache/impl/ReentrantCache.java | 12 +++++++++- .../test/java/cn/hutool/cache/CacheTest.java | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java index 51e044b1c..5775d266d 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java @@ -1,8 +1,11 @@ package cn.hutool.cache.impl; import cn.hutool.core.collection.CopiedIter; +import cn.hutool.core.lang.mutable.Mutable; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import java.util.concurrent.locks.ReentrantLock; /** @@ -81,7 +84,14 @@ public abstract class ReentrantCache extends AbstractCache { public void clear() { lock.lock(); try { - cacheMap.clear(); + // 获取所有键的副本 + Set> keys = new HashSet<>(cacheMap.keySet()); + for (Mutable key : keys) { + CacheObj co = removeWithoutLock(key.get()); + if (co != null) { + onRemove(co.key, co.obj); // 触发资源释放 + } + } } finally { lock.unlock(); } diff --git a/hutool-cache/src/test/java/cn/hutool/cache/CacheTest.java b/hutool-cache/src/test/java/cn/hutool/cache/CacheTest.java index 39a39241c..d7202e0a5 100755 --- a/hutool-cache/src/test/java/cn/hutool/cache/CacheTest.java +++ b/hutool-cache/src/test/java/cn/hutool/cache/CacheTest.java @@ -148,4 +148,26 @@ public class CacheTest { assertFalse(ALARM_CACHE.containsKey(1)); assertEquals(1, counter.get()); } + + /** + * ReentrantCache类clear()方法、AbstractCache.putWithoutLock方法可能导致资源泄露 + * https://github.com/chinabugotech/hutool/issues/3957 + */ + @Test + public void reentrantCache_clear_Method_Test() { + Cache lruCache = CacheUtil.newLRUCache(4); + lruCache.setListener(new CacheListener() { + @Override + public void onRemove(String key, String cachedObject) { + System.out.println(" listener key= "+ key+" value = "+cachedObject.toString()+" cachedObject资源释放操作..."); + } + } ); + lruCache.put("key1","String1"); + lruCache.put("key2","String2"); + lruCache.put("key3","String3"); + lruCache.put("key1","String4");//key已经存在,原始putWithoutLock方法存在资源泄露 + lruCache.put("key4","String5"); + lruCache.clear();//ReentrantCache类clear()方法存在资源泄露 + + } }