强引用不会被GC回收,哪怕内存溢出;软引用在内存不足时才回收,适合内存敏感型缓存;弱引用在每次GC时都可能被回收,用于无泄漏映射;虚引用仅用于跟踪对象回收时间点,必须配合ReferenceQueue使用。
只要对象有强引用指向它,JVM 就认定这个对象“正在使用”,无论当前堆内存多么紧张,GC 都不会回收它。这是最常见、最“霸道”的引用类型。
典型写法:
Object obj = new Object();这里的
obj 就是强引用。只要 obj 还在作用域内(没被赋值为 null,也没超出作用域),对应对象就绝对安全。
容易踩的坑:
OutOfMemoryError: Java heap space
null 或及时释放集合引用(比如静态 Map 不断 put),造成内存泄漏SoftReference 的核心行为是:JVM 在抛出 OutOfMemoryError
之前,会尝试回收所有软引用指向的对象。它比强引用“松”一点,但比弱引用“倔”一点。
实操建议:
ReferenceQueue 监听是否被回收:ReferenceQueue
Object obj = ref.get(); if (obj != null) { ... },因为 get() 可能返回 null
注意:JDK 8+ 中软引用的回收策略受 -XX:SoftRefLRUPolicyMSPerMB 影响,默认每 MB 堆空间保留软引用 1000ms,不是“越晚用越优先保留”——这点常被误解。
WeakReference 的特点是:下一次 GC(哪怕是 Young GC)都可能把它干掉。它不阻止对象被回收,只提供一种“观察式”访问能力。
典型用途:
WeakHashMap 的 key 是弱引用,避免 key 对象无法释放导致 value 永远不能被回收ThreadLocal 内部也依赖弱引用防止内存泄漏)实操要点:
WeakReference.get() 一定返回非空值ReferenceQueue 会收到通知,可用于清理资源PhantomReference 是最特殊的:它的 get() 方法永远返回 null,你根本拿不到它引用的对象。它存在的唯一意义,是在对象被 GC 后,往注册的 ReferenceQueue 里塞一个通知。
真实使用场景极少,但关键:
finalize() 实现更可控、更及时的资源清理(比如直接内存 ByteBuffer 的清理)DirectByteBuffer 中用虚引用 + Cleaner(本质是虚引用增强版)管理堆外内存必须注意:
ReferenceQueue 一起使用,否则毫无意义ReferenceQueue 时,已经处于“finalizable”之后、“已回收”之前的状态,此时对象内存尚未释放,但逻辑上已不可达enqueue 后再通过虚引用访问对象——get() 永远是 null
虚引用的复杂性不在语法,而在于它把对象生命周期的控制权从 JVM 手动交还给程序员,稍有不慎就会漏掉清理或重复清理。真正要用它,往往意味着你在写框架级代码,而不是业务逻辑。