Java强引用、软引用、弱引用、虚引用区别
Java四种引用类型强度依次为强、软、弱、虚引用。强引用宁可OOM也不回收;软引用内存不足时回收,适合缓存;弱引用每次GC即回收,防内存泄漏;虚引用永远无法获取对象,仅用于回收通知配合引用队列。
Java 的四种引用类型,说白了就是 JVM 给程序员提供的一套“对象生死控制权”。它们不是语法糖,而是垃圾回收策略里明确区分出来的语义层级——强度从高到低排下来是:强引用 > 软引用 > 弱引用 > 虚引用。要搞清楚它们的区别,核心就看四个问题:GC 到底收不收?什么时候收?收完之后还能不能拿到对象?以及要不要配合引用队列来用?

强引用:核心业务逻辑的默认选择
先看最熟悉的强引用。平时我们写 Object obj = new Object(),这就是强引用。只要这个变量还活着,对象就死不了——哪怕内存已经快要炸了,JVM 宁愿抛出 OutOfMemoryError 也不会去动它。想让它被回收?只有两条路:要么手动把引用置为 null,要么让这个变量超出作用域(比如方法结束)。业务主逻辑、单例、静态成员这些地方用的都是强引用,但它也是内存泄漏最常见的元凶,不留神就容易把对象“焊死”在内存里。
软引用:内存压力下的缓存策略
再来看软引用。它的策略很“识趣”:内存充足的时候,你就老老实实活着;一旦 JVM 感觉快要顶不住了,比如老年代空间告急、即将 OOM,它就会启动回收。所以软引用特别适合做缓存——图片、模板、计算结果这类东西,用户可能再用,但丢了也不致命,重新算一遍就行。软引用还可以绑定一个 ReferenceQueue,回收后这个引用实例会被自动入队,方便你异步清理缓存条目。不过注意,软引用不是每次 GC 都检查,而是由 JVM 根据堆内存的压力综合判断,回收时机有延迟性和不确定性,别指望它“立刻响应”。
弱引用:一次 GC 即回收的临时缓存
弱引用就更干脆了:只要发生一次 GC(哪怕只是年轻代的 Minor GC),而且对象只剩下弱引用这一条线,二话不说直接回收。它的生命周期极短,适合做临时性、非关键的缓存。典型例子就是 ThreadLocal 的 value 和 WeakHashMap 的 key——用弱引用就是为了防止内存泄漏,让那些不再被使用的 key 能在 GC 时自动消失。同样支持 ReferenceQueue,回收后自动入队,方便做资源清理或状态监听。但有一点要小心:弱引用无法保证对象能活到下一次调用,get() 随时可能返回 null,用之前一定要判空,否则容易踩坑。
虚引用:回收前通知的终极善后工具
最后是虚引用,它最特殊:PhantomReference.get() 永远返回 null,你根本拿不到对象本身。它的存在与否,完全不影响对象的生命周期——只要对象没有被强引用、软引用、弱引用牵着,随时可以被回收。虚引用的唯一用途就是配合 ReferenceQueue,在对象被回收之前、内存释放之前给你一个通知。这个通知可以用来执行 finalize 之外的清理动作,比如关闭文件句柄、释放 off-heap 内存。注意,必须手动传入 ReferenceQueue 构造才能用,否则虚引用就没有任何意义;而且你得主动轮询 ReferenceQueue.poll() 才能拿到回收信号。
引用类型协同:选型决定内存与性能
这四种引用不是替代关系,而是各司其职的协同工具:强引用保核心,软引用缓热数据,弱引用防泄漏,虚引用做善后。用错类型,轻则缓存频繁失效,重则直接 OOM 或资源泄露,所以选型的时候一定要想清楚你到底想“控制”到什么程度。
