在JDK 1.2版之后,Java对引用的概念进行了扩充,将引用分为 强引用(Strongly Re-ference)、 软引用(Soft Reference)、 弱引用(Weak Reference)和 虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。
一、强引用
强引用是最传统的引用,类似Object object = new Object();
这种引用关系。只要强引用关系还存在,垃圾收集器(GC)就永远不会回收掉被引用的对象。
那么什么时候强引用对象才会被GC
回收呢?
如果超出对象的生命周期或者直接令object = null;
这样的话,GC
就会认为该对象不存在引用,可以对其进行回收,具体回收时间取决于GC
算法。
场景:
在一个方法的内部有一个强引用,当方法运行时,这个引用保存在java栈中,而引用的内容保存在java堆中;当方法执行完毕时,出栈,则相应的引用为0,此时,就可以被GC
回收。
public void demo() {
Object object = new Object();
// 省略其他操作
}
如果是定义的对象是全局变量,需要在不用这个对象时,手动对其进行赋值object = null;
,这样,GC
才会回收它。
二、软引用
软引用是用来描述一些还有用,但非必须的对象。当内存空间充足时,GC
就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
如果我们直接调用System.gc();
会回收软引用对象吗?
答案是不会的。软引用对象是在jvm内存不够的时候才会被回收,直接调用System.gc();
只会起到一个通知的作用。
在GC
没有回收它的时候,软引用对象就像强引用对象一样,可以被程序正常访问和使用,但是需要通过软引用对象间接访问,需要的话也能重新使用强引用将其关联。所以软引用适合用来做内存敏感的高速缓存。
String a = new String("123"); // 创建强引用
SoftReference<String> softReference = new SoftReference<String>(a); // 再创建一个软引用关联该对象
a = null; // 消除强引用,现在只剩下软引用与其关联
a = softReference.get(); // 重新关联上强引用
场景:
软引用关联的对象,只有在内存不足的时候JVM才会回收该对象。这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
// 仅做示例
// 引用队列
private ReferenceQueue<T> referenceQueue = new ReferenceQueue<>();
// 保存软引用集合,在引用对象被回收后销毁
private List<Reference<T>> list = new ArrayList<>();
// 构建软引用
Reference<T> reference = new SoftReference<T>(object, referenceQueue);
// 加入软引用集合中
list.add(reference);
注意:SoftReference对象是用来保存软引用的,但它同时也是一个Java对象。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但SoftReference对象本身并不是null,而此时这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。
ReferenceQueue就是用来保存这些需要被清理的引用对象的。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
三、弱引用
弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
场景:
如果一个对象是偶尔(很少)的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用Weak Reference来记住此对象。
// 创建强引用
String a = new String("123");
// 创建弱引用
WeakReference<String> weakReference = new WeakReference<>(a);
// 消除强引用
a = null;
// 弱引用转强引用
a = weakReference.get();
四、虚引用
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在任何时候都可能被垃圾回收器回收。
场景:
虚引用主要用来跟踪对象被垃圾回收器回收的活动。
String a = new String("123");
ReferenceQueue referenceQueue = new ReferenceQueue();
// 创建虚引用,要求必须与一个引用队列关联
PhantomReference phantomReference = new PhantomReference(a, referenceQueue );
虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。