java四种引用类型-强软弱虚

引用类型主要分为强软弱虚四种:(GC是java中的垃圾回收机制)

强引用(StrongReference)

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

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

csharp 复制代码
obj=null //帮助垃圾回收器回收这个对象

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

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

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

ini 复制代码
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还引用着,软引用自身需要配合引用队列来释放。

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

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

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

  • 获取页面进行浏览,浏览完成后就可以将页面设置为软应用
  • 当点击回退按钮到先前浏览过的页面后,判断是否被垃圾回收机制回收了,没有回收就直接用,如果回收了,就在重新构建页面

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

弱引用(WeakReference)

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

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

ini 复制代码
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。

java 复制代码
//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 的四个状态

kotlin 复制代码
  //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。

  1. 创建好了Reference,就进入active状态。
  2. active状态下,如果引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。
  3. Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。
  4. Pending 状态代表等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中入Queue的对象,其状态就变成了Enqueued。
  5. 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运行后终止
相关推荐
bobz96519 分钟前
vxlan 为什么一定要封装在 udp 报文里?
后端
bobz96520 分钟前
vxlan 直接使用 ip 层封装是否可以?
后端
皮皮林5512 小时前
SpringBoot 加载外部 Jar,实现功能按需扩展!
java·spring boot
郑道2 小时前
Docker 在 macOS 下的安装与 Gitea 部署经验总结
后端
3Katrina2 小时前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
rocksun2 小时前
认识Embabel:一个使用Java构建AI Agent的框架
java·人工智能
汪子熙2 小时前
HSQLDB 数据库锁获取失败深度解析
数据库·后端
高松燈2 小时前
若伊项目学习 后端分页源码分析
后端·架构
没逻辑3 小时前
主流消息队列模型与选型对比(RabbitMQ / Kafka / RocketMQ)
后端·消息队列
倚栏听风雨3 小时前
SwingUtilities.invokeLater 详解
后端