前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、引用比较

引用类型 回收时机 是否影响 GC get() 行为 典型用途
强引用 永不回收(只要可达) ✅ 阻止回收 返回对象 普通对象使用
软引用 内存不足时回收 ⚠️ 延迟回收 对象 / null 内存敏感缓存
弱引用 下次 GC 时回收 ❌ 不阻止 对象 / null WeakHashMap、监听器
虚引用 对象回收后入队 ❌ 完全不影响 永远 null 资源清理通知

最佳实践建议

  1. 默认使用强引用,仅在需要特殊生命周期控制时使用其他引用。
  2. 避免使用 finalize(),改用 Cleaner 或 try-with-resources。
  3. 软引用不适合关键缓存(可能随时被回收)。
  4. 虚引用仅用于底层资源管理,业务代码极少直接使用。
  5. 使用 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)回收流程(关键!)

  1. 对象变为不可达(无强/软/弱引用)。
  2. 若对象有 finalize() 方法,JVM 会将其加入 finalization 队列并执行。
  3. finalize() 执行完毕后,对象才真正进入可回收状态。
  4. JVM 回收对象内存
  5. 将对应的 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 执行快)
    }
}

ContactAuthor