软引用是内存压力下自动回收的柔性缓存策略,需配合ConcurrentHashMap、ReferenceQueue及空值校验使用,并依GC类型调优-XX:SoftRefLRUPolicyMSPerMB参数。

软引用的核心定位:内存压力下的柔性缓存
软引用不是“永不回收”的缓存保险丝,而是 JVM 在堆内存趋紧时自动触发回收的柔性策略。它允许对象在内存充足时长期驻留,提升命中率;一旦 GC 检测到内存压力(如老年代使用率升高),就会批量清理软引用指向的对象,防止 OOM。关键在于:它不保证存活,只延缓回收时机——所以不能当作强引用用,每次访问都必须做空值校验和重建准备。
缓存结构必须线程安全且带状态感知
直接用 HashMap
-
ConcurrentHashMap
替代普通 Map,确保多线程读写安全 -
CacheEntry 封装 SoftReference
+ long lastAccessTime,用于模拟 LRU 行为(SoftReference 本身不记录访问) - 构造 SoftReference 时传入 ReferenceQueue,为后续异步清理埋点
读写逻辑要防悬挂、支持懒加载重建
get 不是简单取值,而是“尝试命中 + 失效兜底”:
- 先从 Map 获取 CacheEntry,再调 entry.ref.get()
- 若返回 null,说明已被 GC 回收 → 立即触发业务逻辑重建 value
- 重建后的对象应赋给局部变量或短期强引用,避免刚建完就被 GC 清掉
- put 时不能直接覆盖旧 CacheEntry,否则原 SoftReference 残留可能阻碍回收
失效清理必须主动轮询引用队列
ReferenceQueue 不会自动清理 Map,不处理就会持续膨胀,最终导致内存泄漏:
- 在 put() 或高频 get() 入口轻量调用 queue.poll()(非阻塞)
- 拿到已回收的 SoftReference 后,通过 CacheEntry 中保存的 key 从 Map 中移除对应条目
- 避免使用 remove() 阻塞等待,生产环境推荐 poll() + 空值跳过
JVM 参数需按场景微调
默认参数 -XX:SoftRefLRUPolicyMSPerMB=1000 对多数服务偏激进:每 MB 剩余堆空间仅保留约 1 秒软引用存活期,容易导致缓存频繁失效:
- 若缓存对象较大、重建成本高(如解析后的 XML Schema),可设为 3000~5000,延长平均存活窗口
- 该参数仅对 Parallel GC 和 G1 生效;ZGC/Shenandoah 下软引用会在下次 GC 时立即回收
- 不要无限制拉高(如设成 60000),可能导致老年代滞留大量本该释放的对象,反致 OOM
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











