Java 强引用、软引用、弱引用、虚引用

来源:互联网 发布:仿砍柴网源码破解版 编辑:程序博客网 时间:2024/05/20 06:39

1. 概念

1. 强引用(StrongReference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

2. 软引用(SoftReference)

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

3. 弱引用(WeakReference)

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

4. 虚引用(PhantomReference)

“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用于软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

5. 对象的可及性判断

在多时候,一个对象并不是从根集直接引用的,而是一个对象被其他对象引用,甚至同时被几个对象所引用,从在构成一个以根集为顶得树形结构。如图2所示
这里写图片描述

在这个树形的引用链中,箭头的方向代表了引用的方向,所指向的对象是被引用的对象。由图可以看出,从根集到一个对象可以由很多条路径。比如到达对像的路径就有1-5,3-7两条路径。由此带来了一个问题,那就是某个对象的可达性如何判断:

  • 单条引用路径可达性判断:在这条路径中,最弱的一个引用决定对象的可达性。
  • 多条引用路径可达性判断:几条路径中,最强的一条的引用决定对象的可达性。

比如,我们将设图2种引用1和3为强引用,5为软引用,7为弱引用,对于对象5按照这两个判断原则,路径1-5取最弱的引用5,因此该路径对对象5的引用为软引用。同样,5-7为弱引用。在这两条路径之间取最强的引用,对于对象5是一个软可达对象。

2. 如何使用软引用

SoftReference 的特点是它的一个实例保存对一个 Java 对象的软引用,该软引用的存在不妨碍垃圾收集线程对 Java 对象的回收。也就是说,一旦 SoftReference 保存了对一个 Java 对象的软引用后,在垃圾线程对这个 Java对象回收前,SoftReference类所提供的 get() 方法返回 Java 对象的强引用。另外,一旦垃圾线程回收了该 Java对象之后,get() 方法将返回 null。看下面的代码:

MyOject aRef = new MyObject();SofrReference sSoftRef = new SoftReference(aRef);

此时,对于 MyObject 对象,有两个引用路劲,一个是来自 SoftReference 对象的软引用,一个来自变量 aRef 的强引用,所以这个 MyObject 对象是强可及对象。
随即,我们可以结束 aRef 对这个 MyObject 实例的强引用:

aRef = null;

此后,这个 MyObject 对象成为了软可达对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个 SoftReference 对该对象的引用而始终保留该对象。 Java 虚拟机的垃圾收集线程对软可达对象和其他一般 Java 对象进行了区别对象:软可达对象的清理是由垃圾收集线程根据其特定的算法按照内存需求决定的。舅舅说说,垃圾收集此案成会再虚拟机抛出 OutOfMemoryerror 之前回收软可及对象,而且虚拟机会尽可能优化回收长时间闲置不用的软可达对象,对那些刚刚使用过的软可达对象会被虚拟机尽可能保留。在回收这些对象之前,我们可以通过:

MyObject anotherRef = (MyObject) aSoftRef.get()

重新获得对该实例的强引用。而回收之后,调用 get() 方法就只能得到 null了。

3. 使用 ReferenceQueue 消除失去了软引用对象的 SoftReference

作为一个 Java 对象, SoftReference 对象除了具有保存软引用的特殊性之外,也具有 Java 对象的一般性。所以,当软可及对象被回收之后,虽然这个 SofrRederence 对象的 get() 方法返回 null,但这个 SoftRederence 对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量 SoftReference 对象带来的内存泄漏。在 java.lang.ref 包里还提供了 ReferenceQueue。如果在创建 SoftReference 对象的时候,使用了一个 ReferenceQueue 对象作为参数提供给 SoftRederence 的构造方法,如:

Reference queue = new ReferenceQueue();SoftReference ref = new SoftReference(aMyObject, queue);

那么当这个 SoftReference 所引用的 aMyObject 被垃圾收集器回收的同时,ref所强引用的 SoftReference 对象被列入 ReferenceQueue。也就是说,ReferenceQueue 中保存的对象是 Reference 对象,而且是已经失去了它所软引用的对象的 Reference 对象。另外从 ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的 poll()方法的时候,入股这个队列中不是空队列,那么将返回队列前面的那个 Reference 对象。
在任何时候,我们都可以调用 ReferenceQueue 的 poll() 方法来检查是否它所关心的非强可达对象被回收。如果队列为空,将返回一个 null,否则该方法返回队列中前面的一个 Reference 对象。利用这个方法,我们可以检查哪个 SoftReference 所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的 SofrReference 对象清除掉。常用的方式为:

SoftReference ref = nullwhile ((ref = (SoftReference) q.poll()) != null ) {    // 清除}

4. 软引用示例代码

public class Person {    private Integer id;    private String Name;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return Name;    }    public void setName(String name) {        Name = name;    }    public Person(Integer id) {        this.id = id;        findPersonInfoFromDB(id);    }    private void findPersonInfoFromDB(Integer id) {        this.Name = "test reference person " + id;    }}
import java.lang.ref.ReferenceQueue;import java.lang.ref.SoftReference;import java.util.HashMap;import java.util.Map;public class PersonCache {    static private PersonCache cache;    static private Map<Integer, PersonRef> personMap;    static private ReferenceQueue<Person> q;    private class PersonRef extends SoftReference<Person> {        private Integer _key;        public PersonRef(Person ps, ReferenceQueue<Person> q) {            super(ps, q);            _key = ps.getId();        }    }    private PersonCache() {        personMap = new HashMap<Integer, PersonRef>();        q = new ReferenceQueue<Person>();    }    public static synchronized PersonCache getInstance() {        if (null == cache) {            cache = new PersonCache();        }        return cache;    }    public void cachePerson(Person ps) {        cleanCache();        PersonRef ref = new PersonRef(ps, q);        personMap.put(ref._key, ref);    }    public Person getPersonById(Integer id) {        Person ps = null;        if (personMap.containsKey(id)) {            ps = ((PersonRef) personMap.get(id)).get();        }        if (null == ps) {            ps = new Person(id);            System.out.println("Retrieve From PersonInfoDB. ID=" + id);            this.cachePerson(ps);        }        return ps;    }    public void cleanCache() {        PersonRef ref = null;        while ((ref = (PersonRef) q.poll()) != null) {            personMap.remove(ref._key);        }    }}

参考文章:
1. http://www.2cto.com/kf/201207/139522.html

0 0
原创粉丝点击