From be1ed3d46d8ffde66bcc9861ebea8c314968bf1b Mon Sep 17 00:00:00 2001 From: yukaidi Date: Fri, 29 May 2026 00:32:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(memory):=20ReflectionUtil=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20SoftReference=20+=20TTL=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原代码使用永久缓存 Reflections 实例,占用大量内存且不释放。 改为: - 使用 SoftReference 允许 GC 在内存不足时回收 - 添加 1 小时 TTL 防止长期占用 - 每次获取时自动清理过期条目 --- .../cn/qaiu/vx/core/util/ReflectionUtil.java | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/cn/qaiu/vx/core/util/ReflectionUtil.java b/core/src/main/java/cn/qaiu/vx/core/util/ReflectionUtil.java index 89a48ba..3853bc3 100644 --- a/core/src/main/java/cn/qaiu/vx/core/util/ReflectionUtil.java +++ b/core/src/main/java/cn/qaiu/vx/core/util/ReflectionUtil.java @@ -18,12 +18,14 @@ import org.reflections.util.FilterBuilder; import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; +import java.lang.ref.SoftReference; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.text.ParseException; import java.util.*; +import java.util.concurrent.TimeUnit; import static cn.qaiu.vx.core.util.ConfigConstant.BASE_LOCATIONS; @@ -37,7 +39,26 @@ import static cn.qaiu.vx.core.util.ConfigConstant.BASE_LOCATIONS; public final class ReflectionUtil { // 缓存Reflections实例,避免重复扫描(每次扫描约35K+值,耗时1-3秒,占用大量内存) - private static final Map REFLECTIONS_CACHE = new java.util.concurrent.ConcurrentHashMap<>(); + // 使用 SoftReference 允许 GC 在内存不足时回收,同时添加 TTL 防止长期占用 + private static final Map> REFLECTIONS_CACHE = new java.util.concurrent.ConcurrentHashMap<>(); + private static final long CACHE_TTL_MS = TimeUnit.HOURS.toMillis(1); // 1小时 TTL + private static final Map CACHE_TIMESTAMP = new java.util.concurrent.ConcurrentHashMap<>(); + + /** + * 清理过期的缓存条目 + */ + private static void cleanExpiredCache() { + long now = System.currentTimeMillis(); + Iterator> iterator = CACHE_TIMESTAMP.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (now - entry.getValue() > CACHE_TTL_MS) { + String key = entry.getKey(); + REFLECTIONS_CACHE.remove(key); + iterator.remove(); + } + } + } /** * 以默认配置的基础包路径获取反射器 @@ -49,34 +70,50 @@ public final class ReflectionUtil { } /** - * 获取反射器(带缓存) + * 获取反射器(带缓存,支持 TTL 和 SoftReference) * * @param packageAddress Package address String * @return Reflections object */ public static Reflections getReflections(String packageAddress) { - return REFLECTIONS_CACHE.computeIfAbsent(packageAddress, key -> { - List packageAddressList; - if (key.contains(",")) { - packageAddressList = Arrays.asList(key.split(",")); - } else if (key.contains(";")) { - packageAddressList = Arrays.asList(key.split(";")); - } else { - packageAddressList = Collections.singletonList(key); - } - return createReflections(packageAddressList); - }); + cleanExpiredCache(); + SoftReference ref = REFLECTIONS_CACHE.get(packageAddress); + Reflections reflections = ref != null ? ref.get() : null; + if (reflections != null) { + return reflections; + } + List packageAddressList; + if (packageAddress.contains(",")) { + packageAddressList = Arrays.asList(packageAddress.split(",")); + } else if (packageAddress.contains(";")) { + packageAddressList = Arrays.asList(packageAddress.split(";")); + } else { + packageAddressList = Collections.singletonList(packageAddress); + } + reflections = createReflections(packageAddressList); + REFLECTIONS_CACHE.put(packageAddress, new SoftReference<>(reflections)); + CACHE_TIMESTAMP.put(packageAddress, System.currentTimeMillis()); + return reflections; } /** - * 获取反射器(带缓存) + * 获取反射器(带缓存,支持 TTL 和 SoftReference) * * @param packageAddresses Package address List * @return Reflections object */ public static Reflections getReflections(List packageAddresses) { + cleanExpiredCache(); String cacheKey = String.join(",", packageAddresses); - return REFLECTIONS_CACHE.computeIfAbsent(cacheKey, key -> createReflections(packageAddresses)); + SoftReference ref = REFLECTIONS_CACHE.get(cacheKey); + Reflections reflections = ref != null ? ref.get() : null; + if (reflections != null) { + return reflections; + } + reflections = createReflections(packageAddresses); + REFLECTIONS_CACHE.put(cacheKey, new SoftReference<>(reflections)); + CACHE_TIMESTAMP.put(cacheKey, System.currentTimeMillis()); + return reflections; } private static Reflections createReflections(List packageAddresses) {