JVM之_1_引用
前言
Github:https://github.com/HealerJean
一、引用比较
| 引用类型 | 回收时机 | 是否影响 GC | get() 行为 | 典型用途 |
|---|---|---|---|---|
| 强引用 | 永不回收(只要可达) | ✅ 阻止回收 | 返回对象 | 普通对象使用 |
| 软引用 | 内存不足时回收 | ⚠️ 延迟回收 | 对象 / null | 内存敏感缓存 |
| 弱引用 | 下次 GC 时回收 | ❌ 不阻止 | 对象 / null | WeakHashMap、监听器 |
| 虚引用 | 对象回收后入队 | ❌ 完全不影响 | 永远 null | 资源清理通知 |
最佳实践建议
- 默认使用强引用,仅在需要特殊生命周期控制时使用其他引用。
- 避免使用
finalize(),改用Cleaner或 try-with-resources。 - 软引用不适合关键缓存(可能随时被回收)。
- 虚引用仅用于底层资源管理,业务代码极少直接使用。
- 使用
ReferenceQueue及时清理已回收的引用,避免内存泄漏。
问题:弱引用关联的对象如果还在使用,能被回收吗?”
‘答案:不能。只要对象“还在使用”(即从 GC Roots 可达),即使它被弱引用关联,也不会被回收。
1、强引用(Strong Reference)
1)定义
最常见的引用形式,例如
Object obj = new Object();
只要存在强引用指向一个对象,JVM 绝不会回收该对象,即使内存不足,也会优先抛出 OutOfMemoryError 而非回收。
2)特点
- 默认引用类型。
- 只要对象从 GC Roots 可达(通过强引用链),就不会被回收。
- 方法执行结束后,局部变量销毁 → 强引用消失 → 对象变为不可达 → 可被回收。
3)示例
public class Main {
public static void main(String[] args) {
new Main().fun1();
}
public void fun1() {
Object object = new Object(); // 强引用
Object[] objArr = new Object[1000]; // 强引用数组
// 方法结束,object 和 objArr 销毁,所指向的对象可被 GC 回收
}
}
2、软引用(SoftReference)
1)定义
用于描述 “仍有用但非必需” 的对象。当系统内存充足时,软引用对象不会被回收;当内存不足时,
JVM会在抛出OutOfMemoryError之前回收这些对象。
2)特点
- 适合实现 内存敏感的缓存(如图片缓存、网页缓存)。
get()方法返回对象(若未被回收)或null(若已回收)。-
可配合
ReferenceQueue使用,用于清理已被回收的引用。 - 适用场景:缓存系统(如
Guava Cache 的 softValues)。
3)示例
import java.lang.ref.SoftReference;
import java.lang.ref.ReferenceQueue;
class G {
private int[] big = new int[1_000_000]; // 占用大量内存
}
public class SoftReferenceDemo {
public static void main(String[] args) {
ReferenceQueue<G> queue = new ReferenceQueue<>();
SoftReference<G>[] refs = new SoftReference[1000];
// 创建大量软引用对象(无强引用)
for (int i = 0; i < refs.length; i++) {
refs[i] = new SoftReference<>(new G(), queue);
}
// 检查有多少已被回收
int cleared = 0;
for (SoftReference<G> ref : refs) {
if (ref.isEnqueued()) {
cleared++;
}
}
System.out.println("第一次检查,已回收: " + cleared + " 个");
}
}
3、弱引用(WeakReference)
1)定义
比软引用更弱,描述 “非必需” 的对象。只要发生垃圾回收(无论内存是否充足),弱引用关联的对象都会被回收。
2)特点
- 常用于
WeakHashMap(key 为弱引用,避免内存泄漏)。 get()返回对象或null。- 可配合
ReferenceQueue使用。 - 典型应用:监听器注册表、缓存 key(避免强引用导致无法释放)。
3)示例
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
class G {
private int[] big = new int[1_000_000];
}
public class WeakReferenceDemo {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<G> queue = new ReferenceQueue<>();
WeakReference<G>[] refs = new WeakReference[1000];
for (int i = 0; i < refs.length; i++) {
refs[i] = new WeakReference<>(new G(), queue);
}
int firstClear = 0;
for (WeakReference<G> ref : refs) {
if (ref.isEnqueued()) firstClear++;
}
System.out.println("第一次检查,已回收: " + firstClear + " 个");
System.gc(); // 建议 GC(不保证立即执行)
Thread.sleep(100); // 等待 GC 完成
int secondClear = 0;
for (WeakReference<G> ref : refs) {
if (ref != null && ref.isEnqueued()) secondClear++;
}
System.out.println("第二次检查,新增回收: " + secondClear + " 个");
}
}
4. 虚引用(PhantomReference)
1)正确定义
- 虚引用是最弱的引用,完全不影响对象的生命周期。
- 唯一目的:在对象被 真正回收后 收到系统通知。
- 必须与
ReferenceQueue一起使用,否则无意义。 get()方法永远返回null(这是设计强制行为,防止对象“复活”)。- 虚引用入队发生在对象内存回收之后,而不是之前!
- 警告:
- 不要依赖
finalize(),它已被标记为 deprecated(JEP 421)。 - 虚引用不是“延迟回收”,而是“回收后通知”。
2)回收流程(关键!)
- 对象变为不可达(无强/软/弱引用)。
- 若对象有
finalize()方法,JVM会将其加入 finalization 队列并执行。 finalize()执行完毕后,对象才真正进入可回收状态。JVM回收对象内存。- 将对应的
PhantomReference加入ReferenceQueue。
3)典型用途
- 清理
native资源(如DirectByteBuffer释放堆外内存)。 -
替代
finalize()的安全资源清理机制(如 Java 9+ 的Cleaner)。
4)正确示例
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
class G {
private int[] big = new int[1_000_000];
// 注意:不推荐重写 finalize()!此处仅为演示
@Override
protected void finalize() throws Throwable {
System.out.println("Finalize called");
super.finalize();
}
}
public class PhantomReferenceDemo {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object>[] refs = new PhantomReference[10];
for (int i = 0; i < refs.length; i++) {
Object obj = new G();
refs[i] = new PhantomReference<>(obj, queue);
obj = null; // 显式断开强引用!
}
System.gc();
Thread.sleep(200); // 等待 GC 和 finalize
int enqueued = 0;
for (PhantomReference<?> ref : refs) {
if (ref.isEnqueued()) enqueued++;
}
System.out.println("虚引用入队数量: " + enqueued);
// 输出通常为 10(若 finalize 执行快)
}
}


