java四大引用中,强引用使对象彻底免疫gc,软引用仅在内存告急时回收,弱引用则在任意gc时立即回收;三者本质是gc可达性分析中的不同判定条件。

OpenJDK 的 StrongReference、WeakReference 和 SoftReference 不是“影响 GC 的开关”,而是 GC 决策时的输入条件——GC 会根据引用类型,决定是否把某个对象标记为可回收。同一对象,被不同引用类型持有,存活结果可能完全不同。
强引用让对象彻底“免疫”GC
只要栈帧、静态字段或堆中其他对象还通过强引用链指向它,GC 就不会碰它。哪怕老年代已满、连续触发 Full GC、甚至 OutOfMemoryError 已抛出,JVM 也绝不会回收强引用对象。
常见误操作:
- 忘记将临时集合里的对象置为
null(如list.clear()后未清空数组元素) - 静态
Map持有业务对象,key 或 value 长期不释放 - 线程池中复用线程,
ThreadLocal的value被强引用滞留
这不是 GC “失效”,而是 GC 严格遵守了可达性分析规则:它从 GC Roots 出发,顺着强引用链走,发现对象仍可达,就跳过。
软引用只在内存真正告急时才被清理
SoftReference 的回收时机取决于 JVM 对“即将 OOM”的判断,不是固定某次 GC,也不是按访问顺序淘汰。G1 和 ZGC 的策略差异明显:
- G1 默认更保守,可能等到 old gen 使用率 > 95% 才批量清理软引用
- ZGC 在每次 GC 周期都评估软引用存活意愿,回收更及时但不可预测
-
-XX:SoftRefLRUPolicyMSPerMB参数会影响“软引用存活时长”,但它控制的是“最后一次访问后还能活多久”,不是硬性保底
每次使用前必须检查:if (ref.get() != null)。一旦返回 null,说明已被回收,不能假设缓存一定存在。
弱引用在任意一次 GC 中都可能消失
只要对象只被 WeakReference 持有,下一次 GC(哪怕是 Young GC)就会把它收走。它不看内存压力,只看“有没有非弱引用活着”。
典型陷阱:
-
WeakHashMap的 key 是弱引用,但 value 仍是强引用——如果 value 又反向引用了 key,就会导致 key 实际无法被回收 -
ThreadLocal的Entry键是弱引用,但 get/set 后若不调用remove(),value 会随线程长期驻留 - 误以为
WeakReference.get()返回非空就能“多用几次”,其实 GC 线程随时可能介入
WeakReference 的价值不在“延缓回收”,而在“解耦生命周期”:比如监听器注册后不用手动反注册,靠弱引用+GC 自动断链。
虚引用没列在这里,因为它根本不参与“是否回收”的决策——它只在对象被回收后发通知。而强、软、弱三者的区别,本质是 JVM 在可达性分析中对引用链“有效长度”的判定逻辑不同。写代码时最常踩的坑,不是选错类型,而是忘了它们对引用链的“截断能力”有限:弱引用能断 key,但不断 value;软引用能缓存,但不保命;强引用最稳,但也最危险。











