有哪些引用类型?

在JDK1.2版之后,Java对引用的对象进行了扩充,将引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强度依次减弱。

  • 强引用是最传统的 "引用" 的定义,是指在程序代码之中普遍存在的引用赋值,即类似 "Object obj = new Object()" 这种引用关系。无论任何情况下,只要强引用关系还存在,还可达,垃圾收集器就永远不会回收掉被引用的对象。
  • 软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后提供了SoftReference类来实现软引用。
  • 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2版本之后提供了WeakReference类来实现若引用。
  • 虚引用也称为 "幽灵引用" 或者 "幻影引用",它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来去的一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK1.2版之后提供了PhantomReference类来实现虚引用。
以上摘自《深入理解java虚拟机》

验证

弱引用

思路

定义一个java对象,并且讲该对象和ReferenceQueue放入WeakReference中,分别在gc前后打印WeakReference和WeakReference的引用对象,在gc后获取队列中WeakReference并打印。

预期

gc前,可以获取到WeakReference中引用对象,队列中的WeakReference为空
gc后,获取WeakReference中引用对象为null,队列中的WeakReference可以获取到刚刚创建的WeakReference,拿到标识并且打印

代码

Reference类

public class Reference {

    private String name;

    private Integer age;

    public Reference(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Reference reference = (Reference) o;
        return Objects.equals(name, reference.name) && Objects.equals(age, reference.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Reference{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

WeakReferenceTag类

public class WeakReferenceTag<T> extends WeakReference<T> {

    private String tag;

    public WeakReferenceTag(T referent, ReferenceQueue<? super T> q, String tag) {
        super(referent, q);
        this.tag = tag;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        WeakReferenceTag<?> that = (WeakReferenceTag<?>) o;
        return Objects.equals(tag, that.tag);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(tag);
    }

    @Override
    public String toString() {
        return "WeakReferenceTag{" +
                "tag='" + tag + '\'' +
                '}';
    }
}

运行类

public class WeakReferenceTest {

    public static void main(String[] args) throws InterruptedException {

        // 引用队列
        ReferenceQueue<Reference> referenceQueue = new ReferenceQueue<>();

        // 创建弱引用
        WeakReference<Reference> weakReference = new WeakReferenceTag<>(
                new Reference("name", 13),
                referenceQueue,
                UUID.randomUUID().toString());

        // gc前打印
        System.out.printf("gc前打印reference对象: %s", weakReference);
        System.out.println();
        System.out.printf("gc前获取引用对象:%s", weakReference.get());
        System.out.println();
        System.out.printf("gc前获取队列reference:%s", referenceQueue.poll());
        System.out.println();

        // gc
        System.gc();
        Thread.sleep(1000);

        // gc后获取对象
        System.out.printf("gc后获取引用对象:%s", weakReference.get());
        System.out.println();

        // 查看队列是否收到提醒
        java.lang.ref.Reference<? extends Reference> poll;

        while (true) {
            if ((poll = referenceQueue.poll()) != null) {

                System.out.println("回收队列收到提醒,打印reference:" + poll);
                return;
            } else {
                System.out.println("队列未收到回收提醒,继续等待...");
                Thread.sleep(500);
            }
        }


    }

}

创建一个 Reference 对象,包含name、age属性,创建软引用对象并设置 Reference对象作为引用对象,这里用到UUID作为 weakReference的一个属性(扩展了一下WeakReference,方便队列收到提醒后,打印标识)。

可以看到在第一次打印reference、reference引用对象时可以获取到,并且打印,但是队列打印时为空,说明此刻reference、reference引用对象是存活的,队列是没有收到gc后的提醒的。

image.png

随后在第一次gc后,再次获取引用对象为空,紧接着获取reference引用对象时为null。循环获取队列后,队列收到提醒,并且打印了weakReference标志。

结论

WeakReference作为软引用,其生命周期在下次gc前,垃圾收集器gc后,不管内存是否够用,都会被清除掉,并且会讲该弱引用对象发送到队列中。


Zeran
32 声望4 粉丝

学而不思则罔,思而不学则殆。