JDK中四种对象引用类型

JDK1.2之前,一个对象只有“已被引用”和“未被引用”两种状态,这将无法描述某些特殊情况下的对象,比如,当内存充足时需要保留,而内存紧张时才需要被抛弃的一类对象。

JDK1.2之后,Java对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱,分别介绍一下这4种引用类型。

强引用

Java中默认声明的就是强引用,代码之中普遍存在的类似Object obj=new Object()这类的引用。

只要强引用还存在, 垃圾收集器永远不会回收掉被引用的对象。哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。

如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了。

Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收 obj = null; //手动置null

软引用

一些还有用但并非必需的对象

系统将要发生内存溢出异常之前, 将会把这些对象列进回收范围之中进行第二次回收。 如果这次回收还没有足够的内存, 才会抛出内存溢出异常。

这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。

在 JDK1.2 之后,提供了SoftReference类来实现软引用

/**
 * <p>
 * Title: 创建了 10 个大小为 1M 的字节数组,并赋值给了软引用,然后循环遍历将这些对象打印出来
 * Description: 启动参数:-Xmx5M
 * </p>  *  * @author guodong.li  * @version 1.0.0  * @date 2019/10/9  */ public class TestOom { private static List<Object> list = new ArrayList<>(); public static void main(String[] args) { testSoftReference(); } private static void testSoftReference() { for (int i = 0; i < 10; i++) { byte[] buff = new byte[1024 * 1024]; SoftReference<byte[]> sr = new SoftReference<>(buff); list.add(sr); } System.gc(); //主动通知垃圾回收  for(int i=0; i < list.size(); i++){ Object obj = ((SoftReference) list.get(i)).get(); System.out.println(obj); } } }

 

打印结果:

null
null
null
null
null
null
[B@cb5822
[B@4b9e13df
[B@2b98378d
[B@475530b9

打印结果总是只有后面几个对象被保留,其他的obj全都被置空回收了。这里就说明了在内存不足的情况下,软引用将会被自动回收。

值得注意的一点 , 即使有 byte[] buff 引用指向对象, 且 buff 是一个strong reference, 但是 SoftReference sr 指向的对象仍然被回收了,这是因为Java的编译器发现了在之后的代码中, buff 已经没有被使用了, 所以自动进行了优化。

 

 

弱引用

非必需对象, 但是它的强度比软引用更弱一些。

被弱引用关联的对象只能生存到下一次垃圾收集发生之前。 当垃圾收集器工作时, 无论当前内存是否足够, 都会回收掉只被弱引用关联的对象。

在 JDK1.2 之后,提供了WeakReference类来实现弱引用。

 

import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.List; /**  * <p>  * Title: 创建了 10 个大小为 1M 的字节数组,并赋值给了弱引用,然后循环遍历将这些对象打印出来。  * Description: 启动参数:-Xmx5M  * </p>  *  * @author guodong.li  * @version 1.0.0  * @date 2019/10/9  */ public class TestOom { private static List<Object> list = new ArrayList<>(); public static void main(String[] args) { testWeakReference(); } private static void testWeakReference() { for (int i = 0; i < 10; i++) { byte[] buff = new byte[1024 * 1024]; WeakReference<byte[]> sr = new WeakReference<>(buff); list.add(sr); } System.gc(); //主动通知垃圾回收  for(int i=0; i < list.size(); i++){ Object obj = ((WeakReference) list.get(i)).get(); System.out.println(obj); } } }

运行结果:

null
null
null
null
null
null
null
null
null
null

可以发现所有被弱引用关联的对象都被垃圾回收了。

虚引用

最弱的一种引用关系。

一个对象是否有虚引用的存在, 完全不会对其生存时间构成影响(虚引用并不会决定对象的生命周期,有他没他都一个样),也无法通过虚引用来取得一个对象实例。

虚引用的的对象存活周期并不能确定,对象可能在任何时候被回收。

为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。 换句话说这就是起到了一个哨兵的作用。

在 JDK1.2 之后,提供了PhantomReference类来实现虚引用。

public class PhantomReference<T> extends Reference<T> { public T get() { return null; } public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); } }

通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,如上面所说,将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。

引用队列(ReferenceQueue)

引用队列可以与软引用、弱引用以及虚引用一起配合使用,当垃圾回收器准备回收一个对象时,如果发现它还有引用,那么就会在回收对象之前,把这个引用加入到与之关联的引用队列中去。程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收,这样就可以在对象被回收之前采取一些必要的措施。

与软引用、弱引用不同,虚引用必须和引用队列一起使用。

//引用队列,当引用的对象被回收后,Reference对象本身会被添加到referenceQueue中,
//相当于得到了一个通知
//软引用/弱引用中都有此构造参数,只是在虚引用中此参数变成必传了而已
ReferenceQueue<RefObj> referenceQueue = new ReferenceQueue<>(); RefObj refObj = RefObj("aaa"); PhantomReference<RefObj> ref = new PhantomReference<RefObj>(refObj,referenceQueue); SoftReference<RefObj> ref = new SoftReference<RefObj>(refObj,referenceQueue); WeakReference<RefObj> ref = new WeakReference<RefObj>(refObj,referenceQueue);

 

 

总结

 

程序设计中一般很少使用弱引用与虚引用,使用软用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

猜你喜欢

转载自www.cnblogs.com/liguodong/p/12305744.html