对象的强、软、弱和虚引用

在JDK 1.2之前的版本中,若一个对象不被任何变量引用,那么程序就没法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。java

 

⑴强引用(StrongReference)
强引用是使用最广泛的引用。若是一个对象具备强引用,那垃圾回收器毫不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足的问题。  ps:强引用其实也就是咱们平时A a = new A()这个意思。

⑵软引用(SoftReference)
若是一个对象只具备软引用,则内存空间足够,垃圾回收器就不会回收它;若是内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能够被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用能够和一个引用队列(ReferenceQueue)联合使用,若是软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

⑶弱引用(WeakReference)
弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。不过,因为垃圾回收器是一个优先级很低的线程,所以不必定会很快发现那些只具备弱引用的对象。
弱引用能够和一个引用队列(ReferenceQueue)联合使用,若是弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

⑷虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期,使用Reference.get()方法获得的永远是null,而不像软或者弱引用还可能获得被引用对象obj。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:若是想让虚引用干点实事的话,必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之 关联的引用队列中。算法

package com.jv.interview;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;

public class TestReference {
    public volatile static boolean isRun = true;

    @SuppressWarnings("static-access")
    public static void main(String[] args) throws Exception {
        String abc = new String("abc");
        System.out.println(abc.getClass() + "@" + abc.hashCode());
        final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
        new Thread() {
            public void run() {
                while (isRun) {
                	//只有当垃圾回收被触发后,垃圾回收机制才会把虚引用放入队列,而后经过反射拿到obj实例
                	//可是有一点不明白,既然已经被垃圾回收了,为何还能拿到obj实例喃,应该是从内存清除了啊??????
                    Object obj = referenceQueue.poll();
                    if (obj != null) {
                        try {
                            Field rereferent = Reference.class
                                    .getDeclaredField("referent");
                            rereferent.setAccessible(true);
                            //经过反射的Field拿到对象
                            Object result = rereferent.get(obj);
                            System.out.println("gc will collect:"
                                    + result.getClass() + "@"
                                    + result.hashCode() + "\t"
                                    + (String) result);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }.start();
        
        //绑定虚引用和队列
        PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
                referenceQueue);
        abc = null;
        Thread.currentThread().sleep(3000);
        //触发GC,但不是必须发生
        System.gc();
        Thread.currentThread().sleep(3000);
        isRun = false;
    }
}



程序能够经过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。若是程序发现某个虚引用已经被加入到引用队列,那么就能够在所引用的对象的内存被回收以前采起必要的行动。数据库

 


使用软引用构建敏感数据的缓存
1 为何须要使用软引用

首先,咱们看一个雇员信息查询系统的实例。咱们将使用一个Java语言实现的雇员信息查询系统查询存储在磁盘文件或者数据库中的雇员人事档案信息。做为一个用户,咱们彻底有可能须要回头去查看几分钟甚至几秒钟前查看过的雇员档案信息(一样,咱们在浏览WEB页面的时候也常常会使用“后退”按钮)。这时咱们一般会有两种程序实现方式:一种是把过去查看过的雇员信息保存在内存中,每个存储了雇员档案信息的Java对象的生命周期贯穿整个应用程序始终;另外一种是当用户开始查看其余雇员的档案信息的时候,把存储了当前所查看的雇员档案信息的Java对象结束引用,使得垃圾收集线程能够回收其所占用的内存空间,当用户再次须要浏览该雇员的档案信息的时候,从新构建该雇员的信息。很显然,第一种实现方法将形成大量的内存浪费,而第二种实现的缺陷在于即便垃圾收集线程尚未进行垃圾收集,包含雇员档案信息的对象仍然无缺地保存在内存中,应用程序也要从新构建一个对象。咱们知道,访问磁盘文件、访问网络资源、查询数据库等操做都是影响应用程序执行性能的重要因素,若是能从新获取那些还没有被回收的Java对象的引用,必将减小没必要要的访问,大大提升程序的运行速度。

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

MyObject aRef = new
MyObject();网络

SoftReference aSoftRef=new SoftReference(aRef); 
此时,对于这个MyObject对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量aReference的强引用,因此这个MyObject对象是强可及对象。
随即,咱们能够结束aReference对这个MyObject实例的强引用:性能

aRef = null;.net

此后,这个MyObject对象成为了软可及对象。若是垃圾收集线程进行内存垃圾收集,并不会由于有一个SoftReference对该对象的引用而始终保留该对象。Java虚拟机的垃圾收集线程对软可及对象和其余通常Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError以前回收软可及对象,并且虚拟机会尽量优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽量保留。在回收这些对象以前,咱们能够经过:
MyObject anotherRef=(MyObject)aSoftRef.get(); 
从新得到对该实例的强引用。而回收以后,调用get()方法就只能获得null了。

3 使用ReferenceQueue清除失去了软引用对象的SoftReference
做为一个Java对象,SoftReference对象除了具备保存软引用的特殊性以外,也具备Java对象的通常性。因此,当软可及对象被回收以后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经再也不具备存在的价值,须要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。若是在建立SoftReference对象的时候,使用了一个ReferenceQueue对象做为参数提供给SoftReference的构造方法,如:线程

ReferenceQueue queue = new
ReferenceQueue();code

SoftReference
ref=new
SoftReference(aMyObject, queue); 
那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,并且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也能够看出,它是一个队列,当咱们调用它的poll()方法的时候,若是这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
在任什么时候候,咱们均可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。若是队列为空,将返回一个null,不然该方法返回队列中前面的一个Reference对象。利用这个方法,咱们能够检查哪一个SoftReference所软引用的对象已经被回收。因而咱们能够把这些失去所软引用的对象的SoftReference对象清除掉。经常使用的方式为:对象

SoftReference ref = null;

while ((ref = (EmployeeRef) q.poll()) != null) {

// 清除ref

}

 

原文地址:http://blog.csdn.net/coding_or_coded/article/details/6603549

相关文章
相关标签/搜索