引用的传统定义:
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可达,判定对象是否存活都和“引用”离不开关系。在JDK 1.2版之前,Java里面的引用是很传统的定义: 如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该reference数据是代表某块内存、某个对象的引用。这种定义并没有什么不对,只是现在看来有些过于狭隘了,一个对象在这种定义下只有“被引用”或者“未被引用”两种状态,对于描述一些“食之无味,弃之可惜”的对象就显得无能为力。譬如我们希望能描述一类对象:当内存空间还足够时,能保留在内存之中,如果内存空间在进行垃圾收集后仍然非常紧张,那就可以抛弃这些对象——很多系统的缓存功能都符合这样的应用场景。
扩充:
在JDK 1.2版之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强 度依次逐渐减弱。
除了强引用外,其他的三个引用都可以在java.lang.ref包下找到他们的身影。Reference子类中只有终结器引用是包内可见的,其他3种引用类型均为public,可以在应用程序中直接使用。
强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回
收掉被引用的对象。
public class ReferenceDemo {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = obj1;
obj1 = null;
System.gc(); // 提醒虚拟机,希望进行一次垃圾回收
System.out.println(obj2);
}
}
System.gc():作用就是提醒虚拟机进行一次垃圾回收,但是什么时候回收,还要看虚拟机本身。
软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;//销毁强引用
弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;//销毁强引用
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供了PhantomReference类来实现虚引用。
无法通过虚引用来获取被引用的对象。当试图通过虚引用的 get() 方法取得对象时,总是null。
必须和引用队列一起使用,虚引用在创建时必须提供一个引用队列作为参数。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知应用程序对象的回收情况。
由于虚引用可以跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虛引用中执行和记录
Object obj = new Object();
Reference phantomQueue = new ReferenceQueue();
PhantomReference<Object> pf = new PhanrefertomObject<Object>(obj,phantomQueue);
obj = null;//销毁强引用
终结器引用(Final reference)
它用以实现对象的 finalize() 方法,也可以称为终结器引用。是一种用于确保对象在终结时被正确的处理机制。
用于在垃圾回收之前做一些工作,当一个对象不再被引用时,会调用该对象的finalize方法来进行清理工作。
无需手动编码,其内部配合引用队列使用.
在 GC 时,终结器引用入队。由 Finalizer 线程通过终结器引用找到被引用对象并调用它的finalize() 方法,(可以将对象复活了),finalize方法只能被执行一次,第二次 GC 时才能回收被引用对象。
引用队列ReferenceQueue:
注册的引用型对象(SoftReference、WeakReference、PhantomReference)在垃圾回收器检测到所引用的对象可达性发生改变时,会将这个引用型的对象添加到引用队列中。
创建一个非强引用的引用对象时,可
以传一个引用队列对象给Reference构造函数。引用队列是GC通知程序某个对象不可达的信号,装载这个不可达对象引用的容器。
public class ReferenceQueueDemo {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
ReferenceQueue referenceQueue = new ReferenceQueue();
WeakReference<Object> weakReference = new WeakReference<>(obj,referenceQueue);
obj = null;
System.out.println("=============gc前============");
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println("==============gc后===========");
System.gc();
System.out.println(weakReference.get());
System.out.println(referenceQueue.remove());
}
}
如果一个引用被加入到了引用队列中,那么我们就无法再通过这个引用去找到他引用的对象,调用get()只会返回null。