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

 更新时间:2023年10月25日 11:12:24   作者:闲不住的码  
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的标志
  • 可以用虚引用来管理堆外内存。

Reference和ReferenceQueue

四大引用的父类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 进行关联

  • referent就是Reference实际引用的对象。
  • 通过next属性,可以构建ReferenceQueue。
  • 通过discovered属性,可以构建Discovered List。
  • 通过pending属性,可以构建Pending List。

四大状态

上图就是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

三个Queue/List:ReferenceQueue,discovered List和pending 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运行后终止

以上就是详解Java中的四种引用类型(强软弱虚)的详细内容,更多关于Java引用类型的资料请关注脚本之家其它相关文章!

相关文章

  • JAVA设计模式---单例模式你知道吗

    JAVA设计模式---单例模式你知道吗

    这篇文章主要给大家介绍了关于Java单例模式,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2021-09-09
  • Java获得一个数组的指定长度排列组合算法示例

    Java获得一个数组的指定长度排列组合算法示例

    这篇文章主要介绍了Java获得一个数组的指定长度排列组合算法,结合实例形式分析了java排列组合相关数组遍历、运算操作技巧,需要的朋友可以参考下
    2019-06-06
  • 详解MyBatis中Executor执行SQL语句的过程

    详解MyBatis中Executor执行SQL语句的过程

    MyBatis中获取SqlSession时会创建执行器Executor并存放在SqlSession中,本篇文章将以MapperMethod的execute() 方法作为起点,对MyBatis中的一次实际执行请求进行说明,并结合源码对执行器Executor的原理进行阐释
    2023-07-07
  • Java字符串常量池和intern方法解析

    Java字符串常量池和intern方法解析

    本文主要介绍了Java字符串常量池和intern方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 解决Properties属性文件中的值有等号和换行的小问题

    解决Properties属性文件中的值有等号和换行的小问题

    这篇文章主要介绍了解决Properties属性文件中的值有等号有换行的小问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 深入dom4j使用selectSingleNode方法报错分析

    深入dom4j使用selectSingleNode方法报错分析

    本篇文章是对dom4j使用selectSingleNode方法报错进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Java的CyclicBarrier循环屏障解析

    Java的CyclicBarrier循环屏障解析

    这篇文章主要介绍了Java的CyclicBarrier循环屏障解析,CyclicBarrier和CountDownLatch一样,是一个同步工具类,它允许一组线程相互等待直到达到某个common barrier point,在程序中CyclicBarrier是非常有用的,它适用于一组线程必须互相等待的情况,需要的朋友可以参考下
    2023-12-12
  • Java中Lambda表达式基础及使用

    Java中Lambda表达式基础及使用

    这篇文章主要介绍了Lambda 是JDK 8 的重要新特性。它允许把函数作为一个方法的参数(函数作为参数传递进方法中),使用 Lambda 表达式可以使代码变的更加简洁紧凑,使Java代码更加优雅,感兴趣的小伙伴一起来学习吧
    2021-08-08
  • Spring AbstractRoutingDatasource 动态数据源的实例讲解

    Spring AbstractRoutingDatasource 动态数据源的实例讲解

    本文介绍如何使用 Spring AbstractRoutingDatasource 基于上下文动态切换数据源,因此我们会让查找数据源逻辑独立于数据访问之外
    2021-07-07
  • springcloud之Feign、ribbon如何设置超时时间和重试机制

    springcloud之Feign、ribbon如何设置超时时间和重试机制

    这篇文章主要介绍了springcloud之Feign、ribbon如何设置超时时间和重试机制,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08

最新评论