Java在JDK1.2之后对引用的概念进行了扩充,引入了四种不同的引用。
-
强引用
只有强引用存在,垃圾回收器就永远不会回收调被引用的对象。
使用new来创建一个新对象的方式来创建强引用。
看下面这样的例子
static Object object = new Object();
/**
* 强引用
*/
public static void testStrongReference(){
Object obj = object;
object = null;
System.gc();
System.out.println("after system.gc-strongReference---obj = " + obj);
}
就算你把object进行null操作,也不会让垃圾回收器对对象进行回收。
-
软引用
用来描述一些还有用但是并非必须的对象。
在系统将要发生内存溢出异常之前,将会对这些对象列进回收范围进行第二次回收。
软引用主要用来实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源获取数据,提升速度;当内存不够时,自动删除这部分缓存数据,从真正的来源获取这些数据。
/**
* 软引用
*/
public static void testSoftReference(){
SoftReference<Object> obj = new SoftReference<>(object);
object = null;
System.gc();
System.out.println("after system.gc---softReference = " + obj.get()); }
运行结果:
从上面的例子可以看出就算系统进行的垃圾回收,但是在内存没有发生溢出的时候就不会回收这个对象的。
软引用可以和一个引用队列联合使用,如果引用队列所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加到与之关联的引用队列中去。
软引用在实际中有重要的应用,例如浏览器的后退按钮。
按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。
(1)如果一个网页在浏览结束时就进行内容的回收,则按后退查看前面浏览过的页面时,需要重新构建
(2)如果将浏览过的网页存储到内存中会造成内存的大量浪费,甚至会造成内存溢出
这时候就可以使用软引用
-
弱引用
只能生存到下一次垃圾收集发生之前,当垃圾回收进行工作时,无论内存是否足够,都会被回收。
看这样的例子
情况1:
public static void testWeakReference(){
WeakReference<Object> weakReference = new WeakReference<Object>(object);
WeakReference<Object> weakReferenceStr = new WeakReference<Object>(str);
// object = null;
// str = null;
System.gc();
System.out.println("after system.gc---weakReference = " + weakReference.get());
System.out.println("after system.gc---weakReferenceStr = " + weakReferenceStr.get());
}
可以看出 当对象的引用不为空的时候,垃圾回收不对对象做出任何操作。
情况2:
public static void testWeakReference(){
WeakReference<Object> weakReference = new WeakReference<Object>(object);
WeakReference<Object> weakReferenceStr = new WeakReference<Object>(str);
object = null;
str = null;
//System.gc();
System.out.println("after system.gc---weakReference = " + weakReference.get());
System.out.println("after system.gc---weakReferenceStr = " + weakReferenceStr.get());
}
可以看出:不进行垃圾回收的时候,就算引用置空,也不会对对象进行回收操作。
public static void testWeakReference(){
WeakReference<Object> weakReference = new WeakReference<Object>(object);
WeakReference<Object> weakReferenceStr = new WeakReference<Object>(str);
object = null;
str = null;
System.gc();
System.out.println("after system.gc---weakReference = " + weakReference.get());
System.out.println("after system.gc---weakReferenceStr = " + weakReferenceStr.get());
}
当对象生命周期结束而且进行一次回收操作时,就会发生回收对象。
这里你是不是有个小疑惑?为什么sreTest一直不变?
static String str = "strTest";
因为gc不清理常量池里的垃圾,所以所引用的内容不为null
-
虚引用
虚引用没有什么实在的用途,也无法通过虚引用获得一个对象实例。
为一个对象设置虚引用关联的唯一目的就是这个对象被收集器回收时收到一个系统通知。
public static void testPhantonReference(){
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(object,referenceQueue);
PhantomReference<Object> phantomReferenceStr = new PhantomReference<>(str,referenceQueue);
// object = null;
// str = null;
System.gc();
System.out.println("after system.gc---phantomReference = " + phantomReference.get());
System.out.println("after system.gc---phantomReferenceStr = " + phantomReferenceStr.get());
}
就算引用不为空,也会垃圾回收对象。和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。