跳转到主内容
极星编程网:以代码为星,赴技术山海!

怎么利用 WeakHashMap 实现一个当内存不足时可被自动回收的数组对象引用缓存

WeakHashMap通过弱引用key实现数组缓存的自动回收:以数组为key、附加信息为value,确保无强引用时GC可回收数组并清理条目;若将数组放value中则无法释放。 WeakHashMap 本身不直接缓存数组对象,而是缓存“对数组的弱引用”——更准确地说,它用弱引用作为 key ,从而在 key 所指向的对象(比如某个数组)不再被强引用时,允许该 key-value 对在下一次 GC 后自动从 map 中移除。因此,要实现“内存不足时可被自动回收的数组对象引用缓存”,关键不是把数组放 value 里就完事,而是让数组成为 key(或至少其生命周期由 WeakReference 控制),并配合合理的使用模式。 核心思路:用 WeakReference 包装数组作 key,或直接用数组作 key WeakHashMap 的 key 是弱可达的:只要没有其他强引用指向该 key 对象,GC 就可能回收它,随后对应 entry 会在后续的 map 操作(如 get、put、resize)中被清理。所以最直接的方式是: 把数组对象本身作为 WeakHashMap 的 key(数组是 Object 子类,可作 key); value 存放你真正想缓存的附加信息(比如计算结果、元数据、包装后的视图等); 确保 外部代码不长期持有该数组的强引用 ——否则 GC 不会回收它,WeakHashMap 也就不会失效。 为什么不能把数组放在 value 里? WeakHashMap 只对 key 使用弱引用,value 仍是强引用。如果你写成:
cache.put("key", bigArray);
那么即使 "key" 字符串被回收(其实字符串常量池里一般不会),bigArray 仍被 value 强引用着,完全不会被释放。这违背了“内存敏感缓存”的初衷。 实用示例:缓存大数组的统计摘要 假设你频繁处理不同大 int[],每次都要算 sum/max/min,想缓存结果,但又不希望阻碍数组回收: 以数组对象为 key,摘要对象(如 Summary)为 value; 调用方需注意:处理完数组后,尽快释放对该数组的强引用(比如局部变量离开作用域、置 null); WeakHashMap 内部会在 GC 后自动清理已不可达数组对应的条目。
private final WeakHashMap arraySummaryCache = new WeakHashMap<>(); Summary getSummary(int[] arr) { if (arr == null) return null; // 先查缓存 Summary cached = arraySummaryCache.get(arr); if (cached != null) return cached; // 计算并缓存 cached = new Summary(arr); arraySummaryCache.put(arr, cached); // arr 是 key → 弱引用 return cached; }
进阶:需要 key 稳定且支持非数组对象?用 WeakReference 自定义 key 如果 key 需要复用(比如相同内容的不同数组实例应视为同一逻辑 key),就不能直接用数组对象。此时可封装一个弱引用 key: 自定义 key 类,内部持
WeakReference
和内容哈希(如 Arrays.hashCode(arr)); 重写
equals/hashCode
基于数组内容而非引用; 但要注意:WeakReference#get() 可能返回 null,equals 中需做空检查; 这种方案更灵活,但也更复杂,且仍依赖外部不强持数组。 不复杂但容易忽略:WeakHashMap 的清理是惰性的,依赖后续 map 操作触发 cleanup。若长时间只 put 不 get/size/entrySet,残留的 stale entry 可能暂时占用内存。必要时可手动调用
System.gc()
(仅调试)或定期执行
cache.size()
触发清理。

相关文章