JVM虚拟机(五)强引用、软引用、弱引用、虚引用

目录

引文:

在 Java 中一共存在 4 种引用:强、软、弱、虚。它们主要指的是,在进行垃圾回收的时候,对于不同的引用垃圾回收的情况是不一样的。下面我们就一起来看一下这 4 种引用。

一、强引用

强引用:只有所有 GC Root 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收。

强引用是比较常见的,比如说下面这段代码种,这个 user 就是强引用:

java 复制代码
User user = new User();

强引用的特点有哪些呢?如下图所示:

现在有一个 GC Root,我们之前讲过 GC Root 是用来定位哪些对象是存活的。

  • 假如说 GC Root 关联到了 User 对象,那就证明这个 User 对象是存活的,则这个 User 对象一直都不会被垃圾回收,即使出现了内存不足,抛出 OOM 异常,也不会回收强引用的对象。
  • 只有当 GC Root 不再关联 User 对象,那这个对象才有可能会被垃圾回收器进行回收。

以上就是强引用的特点。


二、软引用

软引用:仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收。

我们先来看一段代码:

java 复制代码
User user = new User();
SoftReference softReference = new SoftReference(user);

在这段代码中,我们先创建了一个 User 对象,然后又创建了一个 SoftReference 对象并且包装了 User 对象。它们的关系如下图所示:

首先 GC Root 能够关联到 SoftReference 对象,然后当前的 SoftReference 又会关联 User 对象。大家注意,SoftReference 对象和 User 对象之间的关联是使用的虚线,因为这种关联属于软引用。

在进行垃圾回收的时候,一开始并不会对 user 对象进行垃圾回收。由于 user 对象是一个软引用,如果在第一次垃圾回收之后内存还是不够,马上又进行了一次垃圾回收,这个时候软引用 User 对象就会被垃圾回收器回收了。

以上就是软引用的介绍,它必须配合 SoftReference 进行使用。


三、弱引用

弱引用:仅有弱引用引用该对象时,在垃圾回收的时候,无论内存是否充足,都会回收弱引用对象。

弱引用和软引用的使用有些类似,我们来看这样一段代码:

java 复制代码
User user = new User();
WeakReference weakReference = new WeakReference(user);

在这段代码中,先创建了一个 User 对象,然后创建了一个 WeakReference 对象并且包装了 User 对象,他们的关系如下图所示:

首先,GC Root 关联到的是 WeakReference 对象,然后由 WeakReference 对象去关联了 User 对象,这里也是用虚线表示的。目前这个 User 对象就是一个弱引用。

在进行垃圾回收的时候。一旦内存不够用了,User 对象作为一个弱引用对象,就会被垃圾回收器回收掉。

关于弱引用有一个经典的例子,就是 ThreaLocal 内存泄露的问题:

在 ThreadLocal 中有一个 Entry 对象,它继承了 WeakReference,然后在构造函数里面调用了 super(k) 方法,也就表示了当前构造函数中的 ThreadLocal<?> 是一个弱引用,一旦内存不够的时候进行了垃圾回收,就会把 k 对象回收掉。但是 value 使用的是 = 进行赋值,就是一个强引用,并不会被垃圾回收器进行回收。所以说这块儿就可能产生内存泄漏。

以上就是弱引用的说明。


四、虚引用

虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存。

为了方便理解,我们来看这样一段代码:

java 复制代码
User user = new User();
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference(user, referenceQueue);

在这段代码中,先创建了一个 User 对象,然后创建了一个 ReferenceQueue 对象(就是一个引用队列),最后创建了一个 PhantomReference 对象并且包装了 User 对象和 ReferenceQueue 对象。它们之间的关系如下图所示:

首先,GC Root 直接关联的是 PhantomReference 虚引用对象,一个是 X,另一个是 Y。这两个 PhantomReference 对象分别去引用了 User1 对象和 User2 对象。

大家可能会想,这里面哪里能体现 ReferenceQueue 队列呢?是这样的,将来如果发生了垃圾回收,把 User1 和 User2 这两个对象回收掉了,那么 PhantomReference 虚引用对象本身在进行垃圾回收发生的时候,会把虚引用对象 X 和 Y 加入到 ReferenceQueue 引用队列中,如下图所示:

把 X 和 Y 虚引用对象加入到 ReferenceQueue 引用队列中之后,引用队列就会配合 Reference Handler 这个线程来去释放虚引用对象所关联的一些外部资源。

比如说 User1 和 User2 已经被垃圾回收掉了,但是回收这两个对象只是释放了 Java 的堆内存资源,它们在使用的过程中有可能会使用一些外部的资源,这些外部资源有可能不是 Java 的内存,有可能使用的是系统的直接内存,那这些内存什么时候释放呢?这些内存必须要等 Java 对象回收掉之后,才能去释放这些外部的资源内存。所以说就需要把这些虚引用对象放入到引用队列中,先记录哪些对象被回收了,然后由 Reference Handler 根据队列的内容去回收资源就可以了。比如我们示例中的 X 和 Y 两个虚引用对象,它们关联的 User 对象已经被回收掉了,这个时候我们也应该把 X 和 Y 对应的外部资源进行释放,有一个专门的线程来进行释放,就叫 Reference Handler。它就会去从引用队列中不断地把这些虚引用对象 X 和 Y 取出来,然后把它们占用的外部资源进行释放。

以上就是虚引用对象的说明了,它需要配合 ReferenceQueue 引用队列才能使用。

补充: 软引用和弱引用也可以通过引用队列去释放自身的资源。

下面我们总结一下这四种引用类型。


五、总结

强引用、软引用、弱引用、虚引用的区别?

  • 强引用:比较常见,只要 GC Root 能关联到,就不会被回收。
  • 软引用:需要配合 SoftReference 使用,当垃圾被多次回收,内存依然不够的时候会回收软引用对象。
  • 弱引用:需要配合 WeakReference 使用,只要进行了垃圾回收,就会把弱引用对象回收。
  • 虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存等外部资源。

整理完毕,完结撒花~🌻

相关推荐
临沂堇14 分钟前
CCF刷题计划——训练计划(反向拓扑排序)
数据结构·c++·算法·拓扑·ccf
铁匠匠匠19 分钟前
【C总集篇】第八章 数组和指针
c语言·开发语言·数据结构·经验分享·笔记·学习·算法
猿饵块21 分钟前
cmake--get_filename_component
java·前端·c++
Unicorn建模22 分钟前
2024“华为杯”中国研究生数学建模竞赛(E题)深度剖析|数学建模完整过程+详细思路+代码全解析
python·算法·数学建模
编程小白煎堆23 分钟前
C语言:枚举类型
java·开发语言
咕咕吖25 分钟前
二叉树的层序遍历(c)
数据结构·算法
王哈哈嘻嘻噜噜29 分钟前
c语言中“函数指针”
java·c语言·数据结构
qq_3391911438 分钟前
spring boot admin集成,springboot2.x集成监控
java·前端·spring boot
苹果酱05671 小时前
通过springcloud gateway优雅的进行springcloud oauth2认证和权限控制
java·开发语言·spring boot·后端·中间件
Sunny_yiyi1 小时前
Gateway--服务网关
java·开发语言·gateway