Java中的四种引用类型(强软弱虚)

Java中的引用类型主要分为四种,分别是强引用、软引用、弱引用和虚引用,这篇文章主要为大家详细介绍了四者的使用与区别

引用(StrongReference)

引用指的就是代码中普遍存在的赋值方式,比如A a = new A()这种。强引用关联的对象,永远不会被GC回收。

当内存空间不足的时候,java虚拟机宁愿抛出OutOfMemoryError 错误,是程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题

obj=null //帮助垃圾回收器回收这个对象

显式地设置obj为null,或者说超出对象的生命周期范围,则GC认为该对象不存在引用,这时候就可以回收这个对象了,具体什么时候回收这要看GC的算法是怎么样的。

当一个方法的内部有一个强引用,这个引用保存在栈中,而这个引用的内容存放在堆中,当这个方法运行完成后就会退出方法栈,那么引用内容也会跟着就会不存在了,这个Object就会被回收。但是如果个这个obj是一个全局变量的时候,就需要再不用的时候将赋值为null,因为强引用是不会被垃圾回收的。

在ArrayList的clear方法中就用到了强引用

private transient Object[] elementData;
 
public void clear() {
    modCount++;
     // clear to let GC do its work
    for (int i = 0; i < size; i++) {
        elementData[i] = null;
    }
    size = 0;
}

在 ArrayList 类中定义了一个私有的变量 elementData 数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。 不同于elementData = null,强引用仍然存在,避免在后续调用 add()等方法添加元素时,进行重新的内存分配。使用如 clear() 方法中释放内存的方法对数组中存放的引用类型特别适用,这样就可以及时释放内存。

引用(SoftReference)

引用可以用SoftReference来描述,指的是那些有用但是不是必须要的对象。系统在发生内存溢出前会对这类引用的对象进行回收。

软引用可以用来实现内存敏感的高速缓存

软引用自身不会被垃圾回收,因为GC Root还引用着,软引用自身需要配合引用队列来释放。

String str = new String("abc"); // 强引用
SoftReference<String> softRef = new SoftReference<String>(str); // 软引用  
// 当内存不足时,等价于:
if (JVM.内存不足()) {//当内存不够的时候就回收
   str = null;  // 转换为软引用
   System.gc(); // 垃圾回收器进行回收
}

软引用在实际中有重要的应用:

浏览器的后退按钮。按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。

  • 获取页面进行浏览,浏览完成后就可以将页面设置为软应用

  • 当点击回退按钮到先前浏览过的页面后,判断是否被垃圾回收机制回收了,没有回收就直接用,如果回收了,就在重新构建页面

软引用可以和一个引用队列(ReferenceQueue)联合使用,当引用的对象被垃圾回收器回收后,JVM会自动把这个软引用加入到和它相关的这个引用队列中

引用(WeakReference)

引用可以用WeakReference来描述,他的强度比软引用更低一点弱引用的对象下一次GC的时候一定会被回收,而不管内存是否足够。 不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。

String str = new String("abc"); //强引用
WeakReference<String> weakRef = new WeakReference<String>(str); // 弱引用
str = null;
 
// 当垃圾回收器进行扫描回收时等价于:
str = null;
System.gc(); // 垃圾回收器进行回收
 
// 下面的代码会让str再次变为一个强引用:
str = weakRef.get();

使用场景:

  • 如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用弱引用来记住此对象。

  • 当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候你就可以使用弱引用。这个引用不会在对象的垃圾回收判断中产生任何附加的影响。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

引用(PhantomReference)

引用也被称作幻影引用,是最弱的引用关系,可以用PhantomReference来描述,他必须和ReferenceQueue一起使用,同样的当发生GC的时候,虚引用也会被回收。

虚引用与其他几种引用都不同,它并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

应用场景:

  • 程序可以通过判断引用队列中是否存在该对象的虚引用,来了解这个对象是否将要被回收。可以用来作为GC回收Object的标志

  • 可以用虚引用来管理堆外内存。

ReferenceReferenceQueue

四大引用的父类Reference和ReferenceQueue

在Reference中有5个非常重要的属性:referent,next,discovered,pending,queue。

//5个非常重要的属性
private T referent;         /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
Reference next;
transient private Reference<T> discovered;  /* used by VM */
private static Reference<Object> pending = null;

可以把每个Reference看做一个节点,多个Reference通过next,discovered,pending 进行关联

2657D8F7-075A-4190-A4FA-B728553B39F6.gif

  • referent就是Reference实际引用的对象。

  • 通过next属性,可以构建ReferenceQueue。

  • 通过discovered属性,可以构建Discovered List。

  • 通过pending属性,可以构建Pending List。

四大状态

F5771F01-D76A-4A02-9FAB-8F561F034CD6.gif

上图就是Reference 的四个状态

  //Reference的两个构造方法,一个带队列,一个不带
Reference(T referent) { 
        this(referent, null);
    }
​
    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
	...
​}

对于带ReferenceQueue的Reference,GC会把要回收对象的Reference放到ReferenceQueue中,后续该Reference需要程序员自己处理 (调用poll方法)

不带ReferenceQueue的Reference, 由GC自己处理,待回收的对象其Reference状态会变成Inactive。

  • 创建好了Reference,就进入active状态。

  • active状态下,如果引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。

  • Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。

  • Pending状态代表等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中入Queue的对象,其状态就变成了Enqueued。

  • Enqueued状态的对象,如果调用poll方法从ReferenceQueue拿出,则该Reference的状态就变成了Inactive,等待GC的回收。

这就是Reference的一个完整的生命周期。

三个Queue/List

  • ReferenceQueue它本质是由Reference中的next连接而成的。用来存储GC待回收的对象。

  • pending List就是待入ReferenceQueue的list。

  • discovered List这个有点特别,在Pending状态时候,discovered List就等于pending List。 在Active状态的时候discovered List实际上维持的是一个引用链。通过这个引用链,我们可以获得引用的链式结构,当某个Reference状态不再是Active状态时,需要将这个Reference从discovered List中删除。

总结

Java的四种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用

回收时机

用途

生存时间

从来不会

对象的一般状态

JVM停止运行时终止

内存不足 时

联合引用队列构造有效期短、占内存大,生命周期长的对象的二级高速缓冲器(内存不足的时候清空)

内存不足 时终止

在垃圾回收时

联合引用队列构造有效期短、占内存大,生命周期长的对象的一级高速缓冲器(发生GC的时候清空)

GC运行后终止

在垃圾回收时

联合引用队列来跟踪对象被垃圾回收器回收的活动

GC运行后终止