写点什么

说一说 Java 中的四种引用类型?

  • 2024-03-25
    福建
  • 本文字数:2782 字

    阅读完需:约 9 分钟

引言


在 JDK1.2 之前 Java 并没有提供软引用、弱引用和虚引用这些高级的引用类型。而是提供了一种基本的引用类型,称为Reference。并且当时 Java 中的对象只有两种状态:被引用和未被引用。当一个对象被引用时,它将一直存在于内存中,直到它不再被任何引用指向时,才会被垃圾回收器回收。而被引用也就是强引用。

而在 JDK1.2 之后对引用的概念进行了扩充,分为了强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference),这 4 种引用的强度依次减弱。他们的关系如下如:


强引用


强引用是 Java 中最常见的引用类型。当你创建一个对象并将其赋值给一个变量时,这个变量会持有该对象的强引用。

Order order = new Order(); // 只要order还指向Order对象,那么Order对象就不会被回收order = null; // 强引用都被设置为 null 时,不可达,则Order对象被回收
复制代码


只要存在强引用指向对象,垃圾回收器将永远不会回收该对象,即使内存不足也不会回收。这可能导致内存溢出,因为即使内存不足,JVM 也不会回收强引用对象。当强引用都被设置为 null 时,对象变成不可达状态,垃圾回收器会在适当的时候将其回收。

比如以下示例,我们创建一个 2M 的数组,但是我们设置 JVM 参数:-Xms2M -Xmx3M,将 JVM 的初始内存设为 2M,最大可用内存为 3M。

public static void main(String[] args) {      //定义一个2M的数组      byte[] objects = new byte[1024 * 1024 * 2];  }
复制代码


此时我们执行方法后,发现报错:



对于强引用,即使内存不够使用,直接报错 OOM,强引用也不会被回收。

对于强引用,就好比生活中,当我们拥有家里的钥匙时,我们可以随时进入你的家,即使我们不需要进入,也能确保我们可以进入。钥匙是我们进入家的强引用。只有当我们不再拥有钥匙时,我们才无法进入家,类似于当没有强引用指向一个对象时,该对象才能被垃圾回收。


软引用


在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。软引用允许对象在内存不足时被垃圾回收器回收。如果一个对象只有软引用指向它,当系统内存不足时,垃圾回收器会尝试回收这些对象来释放内存,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。软引用适用于需要缓存大量对象,但又希望在内存不足时释放部分对象以避免内存溢出的情况,用于实现缓存时,当内存紧张时,可以释放部分缓存对象以保证系统的稳定性。

以下示例我们设置 JVM 参数为:-Xms3M -Xmx5M,然后连续创建了 10 个大小为 1M 的字节数组,并赋值给了软引用,然后循环遍历将这些对象打印出来。

private static final List<Object> list = Lists.newArrayList();    public static void main(String[] args) {      IntStream.range(0, 10).forEach(i -> {          byte[] buff = new byte[1024 * 1024];          SoftReference<byte[]> sr = new SoftReference<>(buff);          list.add(sr);      });        System.gc(); // 主动通知垃圾回收        list.forEach(l -> {          Object obj = ((SoftReference<?>) l).get();          System.out.println("Object: " + obj);      });  }
复制代码


然后我们执行代码之后:



对于打印结果中,只有最后一个对象保留了下来,其他的 obj 全都被置空回收了。即说明了在内存不足的情况下,软引用将会被自动回收。

对于弱引用,就像我们医药箱里的备用药,当我们需要药品时,我们会先看看医药箱里是否有备用药。如果医药箱里有足够的药品(内存足够),我们就可以使用备用药;但如果医药箱里的备用药不够了(内存不足),我们可能会去药店购买。在内存不足时,垃圾回收器可能会回收软引用对象,类似于我们在医药箱里的备用药被用完时去药店购买。


弱引用


JDK1.2 之后,用java.lang.ref.WeakReference来表示弱引用。弱引用与软引用类似,但强度更弱。即使内存足够,只要没有强引用指向一个对象,垃圾回收器就可以随时回收该对象。弱引用适用于需要临时引用对象的场景,如临时缓存或临时存储对象。也可以用于解决对象之间的循环引用问题,避免内存泄漏。

对于上述示例中,我们将数组赋值给弱引用

private static final List<Object> list = Lists.newArrayList();    public static void main(String[] args) {      IntStream.range(0, 10).forEach(i -> {          byte[] buff = new byte[1024 * 1024];          WeakReference<byte[]> sr = new WeakReference<>(buff);          list.add(sr);      });        System.gc(); // 主动通知垃圾回收        list.forEach(l -> {          Object obj = ((WeakReference<?>) l).get();          System.out.println("Object: " + obj);      });  }
复制代码


执行结果发现所有的对象都是 null,即都被回收了。



对于弱引用,就像我们正在旅行,使用一张一次性地图。我们只在需要导航时使用地图,一旦旅行结束,我们就不再需要地图了。这时我们可以选择扔掉地图,类似于弱引用,在垃圾回收器运行时,无论内存是否充足,对象都可能被回收。


虚引用


在 JDK1.2 之后,用java.lang.ref.PhantomReference类来表示虚引用。虚引用是最弱的引用类型,它几乎对对象没有任何影响,不能通过虚引用获取对象,也不能通过它来阻止对象被垃圾回收。从源码中可以看出它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个 null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。



虚引用可以用于在对象被回收时进行后续操作,如对象资源释放或日志记录,常用于跟踪对象被垃圾回收的状态,执行一些清理工作。

而对于弱引用,就像我们去商店,商店入口处的门闩并不直接影响你进入房屋,但它会在有人进入或离开时发出声音,提醒你有人进店(欢迎光临)或者离开(欢迎再来)。类似地,虚引用并不直接影响对象的生命周期,但它可以在对象被回收时发出通知,让你有机会进行一些后续操作,比如资源释放或者记录日志。


引用队列


引用队列(ReferenceQueue)是 Java 中的一个特殊队列,用于配合软引用、弱引用和虚引用,实现更灵活的对象引用和回收管理。

引用队列的主要作用是跟踪对象的垃圾回收过程。当一个软引用、弱引用或虚引用指向的对象被垃圾回收器回收时,如果它们与一个引用队列关联,那么这些引用就会被自动加入到引用队列中。通过监视引用队列中的对象,我们可以了解到对象的回收状态,从而执行一些额外的操作,比如资源释放或日志记录等。


总结


Java 中的四种引用类型各具特点,可根据程序需求选择合适的引用类型。强引用保证对象不被意外回收,软引用和弱引用用于实现缓存或解决内存敏感问题,而虚引用则用于对象回收后的通知和清理操作。合理使用引用类型可以更好地管理内存和避免内存泄漏问题。


文章转载自:码农Academy

原文链接:https://www.cnblogs.com/coderacademy/p/18089036

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
说一说Java中的四种引用类型?_Java_快乐非自愿限量之名_InfoQ写作社区