SoftReference是JVM在内存严重不足、即将抛出OOM前才批量回收的被动缓存机制,非实时响应内存压力;其回收遵循LRU-like策略,依赖-XX:SoftRefLRUPolicyMSPerMB参数与堆空闲量共同决策,需配合ConcurrentHashMap、空值校验及主动清理避免泄漏。

Java 软引用本身不主动“处理”内存不足,而是由 JVM 在检测到内存压力时自动触发回收——它是一种被动响应机制,核心作用是**延缓回收、优先保活,直到 OOM 前一刻才批量清理**。
软引用的回收时机不是“内存一紧就清”
很多人误以为只要堆内存开始紧张,软引用就会立刻失效。实际上:
- JVM 不会在每次 GC(尤其是 Young GC)时回收软引用;
- 它主要在 Full GC 或接近抛出
OutOfMemoryError前,评估当前堆空闲容量后决定是否回收; - HotSpot 默认策略是:每 1MB 堆空闲空间,允许软引用对象最多存活约 1000 毫秒(可通过
-XX:SoftRefLRUPolicyMSPerMB=500调低,让回收更激进); - 越久未访问的软引用,在回收时越可能被优先清理(类似 LRU)。
你必须自己应对 get() 返回 null 的情况
软引用不提供自动 fallback,每次访问都要检查是否已被回收:
-
不能直接链式调用:比如
ref.get().toString(),一旦get()返回null就 NPE; - 必须显式判空并重建:查不到就重新加载/创建对象,再塞回缓存;
- 推荐配合 ConcurrentHashMap:避免并发 put/get 导致覆盖或丢失;
- 示例逻辑:
SoftReference<Data> ref = cache.get(key); Data data = (ref != null) ? ref.get() : null; if (data == null) { data = loadFromDB(key); // 重建 if (data != null) cache.put(key, new SoftReference<>(data)); }
避免缓存“假存活”和内存泄漏
软引用只是 value 的持有方式,但整个缓存结构仍需主动维护:
立即学习“Java免费学习笔记(深入)”;
-
不要用 SoftReference 当 key:HashMap/ConcurrentHashMap 的 key 必须稳定,而软引用对象一旦被回收,
equals()/hashCode()行为不可靠,会导致 key 匹配失败、条目堆积; -
强引用 value 会彻底失效软引用:比如把
ref.get()结果赋给静态变量或长期存活的集合,JVM 就再也不会回收它; -
定期清理失效 entry:可借助
ReferenceQueue监听回收事件,异步移除 map 中已失效的键;或在每次get()时顺手清理ref.get() == null的项。
实际效果取决于 JVM 配置和堆大小
软引用行为高度依赖运行时环境:
- 堆太小(如
-Xmx256m),软引用几乎无法存活,退化为“伪弱引用”; - 建议搭配合理堆设置(如
-Xmx2g -XX:+UseG1GC)和 GC 日志(-XX:+PrintGCDetails)观察回收时机; - 在 Android 等受限环境,软引用回收更不可控,生产环境图像类缓存建议改用
LruCache或Guava Cache。


















