java 的四种引用类型

2022-06-27 12:26:38 浏览数 (1)

概述

在 java 对象存活判定算法的文章中,我们介绍了java 引用的分类。= 本文中,我们深入讨论一下四种引用究竟有什么区别,以及如何指定具体的引用方式。

正如在上面日志中介绍的,在Java中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了。

强引用

代码中普遍存在的,Object obj = new Object() 所创建的引用,只要强引用存在,垃圾收集器就永远不会回收被引用对象。 当内存不足的时候,jvm 就会抛出 OutOfMemory 错误,而不会回收强引用的对象。

强引用的断裂

只有以下两种方式可以让强引用中断,从而让 jvm 在合适的时间就会回收该对象。 1. 显式赋值为 null 会中断强引用和对象之间的关联 2. 在一个方法内部创建的对象,由于强引用保存在栈上,所引用的对象保存在堆空间中,当这个方法运行完成就会退出方法栈,从而让强引用断裂

软引用

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。 通常,软引用可用来实现内存敏感的高速缓存。

创建软引用

代码语言:javascript复制
String str = new String("abc"); // 强引用SoftReference<String> softRef = new SoftReference<String>(str); // 软引用

适用场景

正如我们前面说的,软引用最常用的场景是高速缓存。 例如对于已经加载过的网页,如果我们通过强引用来实现,在进入新页面时,老页面就有两种选择:主动赋值为 null 或什么都不做。 如果赋值为 null,那么点击回退就必须重新加载原网页,造成体验不佳,而如果什么都不做,大量的加载过的网页停留在内存中,最终就会造成 OutOfMemory 错误。 这时,通过软引用,让已加载过的页面在内存不足时被自动回收,只要内存充足就可以任意回退而不需要重新加载,就是一个非常好的选择。

软引用队列

软引用可以配合引用队列进行使用,当软引用占有的对象被回收后,jvm 会将该软引用对象放入引用队列中。 通过软引用队列,我们可以清楚地知道程序中已有的软引用哪些已经被回收掉了。

代码语言:javascript复制
SoftReference<T> ref = new SoftReference<T>(T t, ReferenceQueue refQueue);

通过软引用队列实现软引用缓存

我们结合软引用与软引用队列来实现一个 KV 缓存。

缓存接口 我们先来定义最基本的 KV 缓存规范接口。

只需要一个 get 接口和一个 set 接口即可。

软引用的实现 软引用只是对一个对象的引用,我们需要 K-V 的组合,因此仅仅使用软引用是不够的,需要对软引用添加额外信息,因此我们继承软引用来实现自己的软引用。

软引用缓存

我们通过 ConcurrentHashMap 实现了一个 K-V 存储,同时通过 ExtraInfoReference 设置 info 成员为 key,将软引用设置为 value 的方式记录了所有在缓存中的数据。 这样,当我们调用 get 方法发现 value 为 null 的时候,我们可以轻松地通过软引用队列来对缓存进行清理。 需要注意的是,调用 get 方法时,value = refValue.get() 会将软引用转化为强引用。

弱引用

与软引用相比,只具有弱引用的对象拥有更短暂的生命周期,不管当前内存空间是否充足,只要进行垃圾回收,弱引用的对象都会被回收。 通常只有创建时不一定会被使用到的对象应该设置为弱引用,只有当被使用到时,即通过赋值,转化为强引用,例如回调方法中的对象是一个典型场景,回调方法通常是通过匿名内部类实现的,如果他们持有外部类的强引用就造成了内存泄露。

代码语言:javascript复制
WeakReference<T> ref = new WeakReference<T>(T t);

弱引用队列

与软引用一样,也可以通过传入 ReferenceQueue 对象指定销毁后存放的队列。

代码语言:javascript复制
WeakReference<T> ref = new WeakReference<T>(T t, ReferenceQueue refQueue);

虚引用

虚引用也称为“幽灵引用”或“幻影引。 无法通过虚引用获取一个对象的实例,因此它的存在只能用来跟踪垃圾回收器的回收活动。 虚引用必须与引用队列联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

代码语言:javascript复制
PhantomReference<T> ref
    = new PhantomReference<T>(T t, ReferenceQueue refQueue);

总结

四种引用类型及生存时间

引用类型

被回收时间

生存时间

备注

强引用

不会被回收

jvm 停止运行时终止

普通赋值

软引用

内存不足时回收

内存不足时 gc 后终止

通常用作对象缓存

弱引用

垃圾回收时被回收

gc 运行后终止

通常用作防止内存泄露,如匿名内部类对外部类对象的引用

虚引用

不能实例化

参考资料

深入理解 Java 虚拟机 — jvm 高级特性与最佳实践(第 2 版) https://www.cnblogs.com/gudi/p/6403953.html。 https://blog.csdn.net/jiangjiajian2008/article/details/52929843。

0 人点赞