Java Reference(SoftReference、WeakReference、PhantomReference)的使用

Reference简介



Reference继承自Object,有SoftReference、WeakReference、PhantomReference三个直接子类。

三个子类的构造方法中涉及到ReferenceQueue和Referent:

● Referent:被引用的对象

● ReferenceQueue:当引用(软引用/弱引用/虚引用)的Referent被回收后,该引用(软引用/弱引用/虚引用)会被enqueue到这个ReferenceQueue中


方便测试,我们新建一个类ReferenceTest,再创建一个静态内部类Person,重写内部类的 finalize() 方法[ GC collector回收对象时调用此方法 ]

public class ReferenceTest {

    public static void main(String[] args) {
    
    }

    static class Person {

        private String name;

        public String getName() {
            return name;
        }

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

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("in Person finalize");
        }
    }

}


1.  StrongReference强引用


Java中使用最多。普通的引用Object o = new Object();   Person person = new Person();  等就是强引用。

强引用本身存储在栈中,new出来的对象存储在堆中。栈中保存的引用指向堆中对象的地址。

一般情况下,当引用不再指向堆中对象的地址时(如person = null;),GC collector就开始考虑对此内存(堆中的对象)进行回收。

Person person = new Person();

person就是是一个强引用,强引用不会被GC,即使内存不够抛出OOM时也不会被回收。


2.  SoftReference软引用


软引用普通使用形式:

    Person person = new Person();
    SoftReference<Person> sr = new SoftReference<Person>(person);

强引用person作为参数,创建了一个软引用对象sr。看下面的例子:

    private static void testSoftReference() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        SoftReference<Person> sr = new SoftReference<Person>(person);
        person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }

        System.gc();

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }
    }
执行上述的例子,结果如下:
person对象为com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
①当执行person = null 后,堆内存的Person对象不再有任何强引用指向它,但此时还存在 sr引用的对象 指向Person对象。

此时调用sr.get()方法,返回Person对象,即之前堆中的强引用对象。我们可以合理猜测GC collector很有可能尚未进行垃圾回收,所以此时sr.get()方法返回不为空。

②我们继续执行System.gc() ,强制进行垃圾回收。打印结果可以看到,sr.get() 返回依然不为空,说明Person对象依旧没有被回收。


软引用所指向的对象要进行回收,需要满足两个条件:

●   没有任何强引用 指向 软引用指向的对象(内存中的Person对象)

●   JVM需要内存时,即在抛出OOM之前

总结:SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。


软引用还可以和一个ReferenceQueue一起使用, 当SoftReference的Referent被回收以后,这个SoftReference会被自动enqueue到这个ReferenceQueue中。

    private static void testSoftReferenceWithQueue() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        ReferenceQueue<Person> queue = new ReferenceQueue<>();
        SoftReference<Person> sr = new SoftReference<Person>(person, queue);
        person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }
        System.out.println("加入ReferenceQueue的对象为" + queue.poll());

        System.gc();

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }
        System.out.println("加入ReferenceQueue的对象为" + queue.poll());
    }
执行上述的例子,结果如下:
person对象为com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
加入ReferenceQueue的对象为null
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
加入ReferenceQueue的对象为null

注意:当SoftReference或WeakReference的get()方法返回null时,仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。

而只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中。


3.  WeakReference弱引用


弱引用的一般使用形式:

    private static void testWeakReference() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        WeakReference<Person> wr = new WeakReference<>(person);
        person = null;
        //被GC后,之前new出的Person对象会立即被回收,进入GC流程

        if (wr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }

        System.gc();

        if (wr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }
    }
执行结果为:
person对象为com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
person对象进入GC流程
in Person finalize

①当执行person = null 后,堆内存的Person对象不再有任何强引用指向它,但此时还存在 wr引用的对象 指向Person对象。

此时调用wr.get()方法,返回Person对象,即之前堆中的强引用对象。我们可以合理猜测GC collector很有可能尚未进行垃圾回收,所以此时wr.get()方法返回不为空。

②我们继续执行System.gc() ,强制进行垃圾回收。打印结果可以看到,wr.get() 返回为空“person对象进入GC流程”,且执行了静态内部类中的finalize方法。说明Person对象被回收,进入垃圾回收流程。


弱引用所指向的对象要进行回收,只需要满足条件:

●  没有任何强引用 指向 弱引用指向的对象(内存中的Person对象)

总结:WeakReference不改变原有的强引用对象的垃圾回收机制。一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。


其主要使用场景见于:当前已有强引用指向强引用对象,此时由于业务需要,需要增加对此对象的引用,同时又不希望改变此引用的垃圾回收时机,此时WeakReference正好符合需求,常见于一些与生命周期的场景中,比如Activity中Handler的使用,为了防止内存泄漏需要用到弱引用。


与SoftReference一样,可以同ReferenceQueue一起使用。当WeakReference的Referent被回收以后,这个WeakReference会被自动enqueue到这个ReferenceQueue中。

    private static void testWeakReferenceWithQueue() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        ReferenceQueue<Person> queue = new ReferenceQueue<>();
        WeakReference<Person> wr = new WeakReference<Person>(person, queue);
        System.out.println("wr对象为" + wr);

        person = null;

        if (wr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }
        System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
        System.out.println("queue item:" + queue.poll());

        System.gc();

        if (wr.get() == null) {//仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }
        try {
            //确保垃圾回收线程能够执行
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
        System.out.println("queue item:" + queue.poll());
    }
执行结果:
person对象为com.example.ReferenceTest$Person@511d50c0
wr对象为java.lang.ref.WeakReference@60e53b93
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
Whether or not this reference has been enqueued: false
queue item:null
person对象进入GC流程
in Person finalize
Whether or not this reference has been enqueued: true
queue item:java.lang.ref.WeakReference@60e53b93
从第二行和最后一行可以看出,person进入GC流程后,wr被加入了queue中。

注意:当SoftReference或WeakReference的get()方法返回null时,仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。

而只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中。


4.PhantomReference虚引用


我们来看看虚引用的源码:

package java.lang.ref;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;

public class PhantomReference<T> extends Reference<T> {
    public T get() {
        return null;
    }

    public PhantomReference(T var1, ReferenceQueue<? super T> var2) {
        super(var1, var2);
    }
}

●   PhantomReference只有一个构造函数PhantomReference(T referent, ReferenceQueue<? super T> q),因此,PhantomReference使用必须结合ReferenceQueue;

●   不管有无强引用指向PhantomReference的指示对象,PhantomReference的get()方法返回结果都是null。

看下面的例子:

    private static void testPhantomReference() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        ReferenceQueue<Person> queue = new ReferenceQueue<>();
        PhantomReference<Person> pr = new PhantomReference<>(person, queue);
        System.out.println("pr对象为" + pr);
        System.out.println("pr.get()=" + pr.get());

        person = null;

        System.gc();

        try {
            //确保垃圾回收线程能够执行
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("queue item:" + queue.poll());
    }
我的执行结果为:
person对象为com.example.ReferenceTest$Person@511d50c0
pr对象为java.lang.ref.PhantomReference@60e53b93
pr.get()=null
in Person finalize
queue item: null
从第四行可以看出,Person对象已经进入了GC流程;第五行打印出的为null,说明GC流程还没有执行完,pr还未加入queue中。

理想的执行结果应该为:

person对象为com.example.ReferenceTest$Person@511d50c0
pr对象为java.lang.ref.PhantomReference@60e53b93
pr.get()=null
in Person finalize
queue item: java.lang.ref.PhantomReference@60e53b93


总结:与WeakReference一样,不改变原有的强引用对象的垃圾回收机制。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在 任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动[ 监听并采取必要的行动 ]。


虚引用的用途:

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。

程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。



附件:整个测试类

package com.example;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class ReferenceTest {

    public static void main(String[] args) {
        //testSoftReference();
        //testSoftReferenceWithQueue();
        //testWeakReference();
        //testWeakReferenceWithQueue();
        //testPhantomReference();
    }

    /**
     * 虚引用
     */
    private static void testPhantomReference() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        ReferenceQueue<Person> queue = new ReferenceQueue<>();
        PhantomReference<Person> pr = new PhantomReference<>(person, queue);
        System.out.println("pr对象为" + pr);
        System.out.println("pr.get()=" + pr.get());

        person = null;
        System.out.println("queue item:" + queue.poll());

        System.gc();

        try {
            //确保垃圾回收线程能够执行
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("queue item: " + queue.poll());
    }

    /**
     * 弱引用
     */
    private static void testWeakReferenceWithQueue() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        ReferenceQueue<Person> queue = new ReferenceQueue<>();
        WeakReference<Person> wr = new WeakReference<Person>(person, queue);
        System.out.println("wr对象为" + wr);

        person = null;

        if (wr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }
        System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
        System.out.println("queue item:" + queue.poll());

        System.gc();

        if (wr.get() == null) {//仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }
        try {
            //确保垃圾回收线程能够执行
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
        System.out.println("queue item:" + queue.poll());
    }


    /**
     * 弱引用
     */
    private static void testWeakReference() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        WeakReference<Person> wr = new WeakReference<>(person);
        person = null;
        //被GC后,之前new出的erson对象会立即被回收,进入GC流程

        if (wr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }

        System.gc();

        if (wr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + wr.get());
        }
    }

    /**
     * 软引用
     */
    private static void testSoftReferenceWithQueue() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        ReferenceQueue<Person> queue = new ReferenceQueue<>();
        SoftReference<Person> sr = new SoftReference<Person>(person, queue);
        person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }
        System.out.println("加入ReferenceQueue的对象为" + queue.poll());

        System.gc();

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }
        System.out.println("加入ReferenceQueue的对象为" + queue.poll());
    }

    /**
     * 软引用
     */
    private static void testSoftReference() {
        Person person = new Person();
        System.out.println("person对象为" + person);

        SoftReference<Person> sr = new SoftReference<Person>(person);
        person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }

        System.gc();

        if (sr.get() == null) {
            System.out.println("person对象进入GC流程");
        } else {
            System.out.println("person对象尚未被回收" + sr.get());
        }
    }


    static class Person {

        private String name;

        public String getName() {
            return name;
        }

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

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println("in Person finalize");
        }
    }

}




参考链接:

http://www.cnblogs.com/lwbqqyumidi/p/4151833.html

http://www.cnblogs.com/zemliu/p/3333499.html

http://wenwen.sogou.com/z/q747019591.htm

http://docs.oracle.com/javase/8/docs/api/java/lang/ref/package-summary.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值