java中的弱引用软引用和虚引用

 在java中的java.lang.ref包中定义了三个引用类,分别是软引用、弱引用、和虚引用。这3个类提供了一种便捷的机制让我们可以和垃圾回收机制交互,同时也为缓存提供了一种机制,那么这三个类导致有什么作用呢?


SoftReference:

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。

    软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。


WeakReference:

弱引用与软引用的区别在于:弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

    弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。


PhatomReference:

虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。


所以从这些定义上可以看出,Reference提供了一种机制,当强引用消失之后,弱引用是否可以取到保存到head中对象,这其中需要考虑的因素就是系统gc机制。


下面通过几个例子来了解这些Reference的使用机制。

1.强引用存在

 ReferenceQueue<Person> queue = new ReferenceQueue<Person>();

    Person softPerson = new Person();
    softPerson.setId(1);
    softPerson.setAge(18);
    softPerson.setName("zhangsan");


    Person weakPerson = new Person();
    weakPerson.setId(2);
    weakPerson.setAge(19);
    weakPerson.setName("lisi");


    Person phantomPerson = new Person();
    phantomPerson.setId(3);
    phantomPerson.setAge(20);
    phantomPerson.setName("wangwu");


    SoftReference<Person> softRef = new SoftReference<Person>(softPerson);
    WeakReference<Person> weakRef = new WeakReference<Person>(weakPerson);
    PhantomReference<Person> phantomRef = new PhantomReference<Person>(phantomPerson, queue);

  
    ////////////////////softrefernece////////////////////////////////////
    try {
       System.out.println(softRef.get().getName());
    } catch (Exception e) {
        e.printStackTrace();
    }
    ////////////////////weakreference///////////////////////////////////
    try {
        System.out.println(weakRef.get().getName());
    } catch (Exception e) {
        e.printStackTrace();
    }
    ////////////////////phatomReference/////////////////////////////////
    try {
        System.out.println(phantomRef.get().getName());
    } catch (Exception e) {
        e.printStackTrace();
    }

}
zhangsan
java.lang.NullPointerException
lisi
	at com.app.client.RefClient.main(RefClient.java:53)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

在强引用依然存在的情况下,虚引用(PhatomReference)的get()方法返回的竟然是null,为什么呢?

public class PhantomReference<T> extends Reference<T> {

    /**
     * Constructs a new phantom reference and registers it with the given
     * reference queue. The reference queue may be {@code null}, but this case
     * does not make any sense, since the reference will never be enqueued, and
     * the {@link #get()} method always returns {@code null}.
     *
     * @param r the referent to track
     * @param q the queue to register the phantom reference object with
     */
    public PhantomReference(T r, ReferenceQueue<? super T> q) {
        super(r, q);
    }

    /**
     * Returns {@code null}.  The referent of a phantom reference is not
     * accessible.
     *
     * @return {@code null} (always)
     */
    @Override
    public T get() {
        return null;
    }
}

从源码上可以看到get()方法永远都是返回null,那么这个PhatomReference类到底有什么用呢,这个我们放在最后讲解。现在只考虑SoftReference和WeakReference两个引用。


2.断开强引用

softPerson = null;
weakPerson = null;

////////////////////softrefernece////////////////////////////////////
try {
   System.out.println(softRef.get().getName());
} catch (Exception e) {
    e.printStackTrace();
}
////////////////////weakreference///////////////////////////////////
try {
    System.out.println(weakRef.get().getName());
} catch (Exception e) {
    e.printStackTrace();
}
zhangsan
lisi

从执行结果上看到,即使这个时候强引用断开了,但是依然可以引用到Head中的对象。在没有gc的情况下,SoftReference和WeakRefenece的作用一致。


3.系统gc之后

softPerson = null;
weakPerson = null;
System.gc();
////////////////////softrefernece////////////////////////////////////
try {
   System.out.println(softRef.get().getName());
} catch (Exception e) {
    e.printStackTrace();
}
////////////////////weakreference///////////////////////////////////
try {
    System.out.println(weakRef.get().getName());
} catch (Exception e) {
    e.printStackTrace();
}
java.lang.NullPointerException
zhangsan
	at com.app.client.RefClient.main(RefClient.java:49)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

从结果上,可以看到在垃圾回收机制运行之后,weakReference所引用的在Head中的对象被垃圾回收了,但是SoftReference在head中引用的对象依然存在。那SoftReference引用的对象在什么时候被回收呢?只要到了内存非常小的情况下才会被回收。

从上面的例子可以看出: 强引用 》 SoftReference >WeakReference .


下面再来说说PhatomRefernece类的使用,这个引用与其他两个引用最大不同就是先将引用加入到引用队列中,然后在回收Head中的对象,这样我们可以在引用队列中监视gc即将回收的对象。

public class PhatomRefClient {

    public static void main(String[] args) {

        Person p = new Person();
        p.setId(1);
        p.setName("zhaosi");
        p.setAge(17);
        System.out.println(p);
        ReferenceQueue<Person> queue = new ReferenceQueue<Person>();
        PhantomReference<Person> phaRef = new PhantomReference<Person>(p, queue);

        p = null;
        System.gc();
        
        while (true) {
            Object o = queue.poll();
            if (o != null) {
                try {
                    Field rereferent = Reference.class
                            .getDeclaredField("referent");
                    rereferent.setAccessible(true);
                    Person result = (Person)rereferent.get(o);
                    System.out.println("system gc will kill person:" +result.getName());
                    System.out.println(result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
com.app.bean.Person@5ced6f0d
system gc will kill person:zhaosi
com.app.bean.Person@5ced6f0d


实现简单的缓存系统

public class PersonCache {
    private Hashtable<Integer, PersonRef> mCache = null;
    private ReferenceQueue<Person> queue;
    private static PersonCache instance;

    private PersonCache() {
        mCache = new Hashtable<Integer, PersonRef>();
        queue = new ReferenceQueue<Person>();
    }

    public static PersonCache getInstance() {
        if (instance == null) {
            instance = new PersonCache();
        }
        return instance;
    }


    public void cachePerson(Person p) {
        int id = p.getId();
        PersonRef ref = new PersonRef(p, queue);
        mCache.put(id, ref);
    }


    public Person getPersonById(int id) {
        Person p = null;
        p = ((PersonRef) mCache.get(id)).get();
        if (p == null) {
            mCache.remove(id);
            clearCache();
            p = new Person(id);
            System.out.println("create a new Person");
            cachePerson(p);
        }
        return p;
    }

    private void clearCache() {
        while (queue.poll() != null) ;
    }

    //inside a _key
    class PersonRef extends WeakReference<Person> {
        int _key;

        public PersonRef(Person referent, ReferenceQueue<? super Person> q) {
            super(referent, q);
            _key = referent.getId();
        }
    }
}
public class client {

    public static void main(String args[]){

        Person p1 = new Person();
        p1.setId(1);
        p1.setName("wahaha1");
        p1.setAge(22);

        Person p2 = new Person();
        p2.setId(2);
        p2.setName("wahaha2");
        p2.setAge(22);

        Person p3 = new Person();
        p3.setId(3);
        p3.setName("wahaha3");
        p3.setAge(22);

        Person p4 = new Person();
        p4.setId(4);
        p4.setName("wahaha4");
        p4.setAge(22);

        PersonCache pc = PersonCache.getInstance();
        pc.cachePerson(p1);
        pc.cachePerson(p2);
        pc.cachePerson(p3);
        pc.cachePerson(p4);

        p1 = null;
        p2 = null;
        p3 = null;
        p4 = null;


        System.out.println(pc.getPersonById(1).getName());
        System.out.println(pc.getPersonById(2).getName());
        System.out.println(pc.getPersonById(3).getName());
        System.out.println(pc.getPersonById(4).getName());

    }
}

wahaha1
wahaha2
wahaha3
wahaha4

balalala~~

你可能感兴趣的:(java,SoftReference,WeakReference,PhantomRefence)