一次性把Java的四种引用说清楚!

当前位置:首页 > 广场 > 一次性把Java的四种引用说清楚!

一次性把Java的四种引用说清楚!

2024-11-14广场30

深度解读Java四种引用类型:强、软、弱与虚引用

一次性把Java的四种引用说清楚!

在编程世界中,引用类型的选择关乎内存管理和程序性能。近日在CodeReview时,我再次被Java中的四种引用类型所吸引,它们分别是强引用、软引用、弱引用和虚引用。回想起上次深入学习这些引用类型还是在面试准备阶段,尽管平时使用不多,但它们的重要性不容忽视。今天,我决定彻底理清这些概念。

让我们明白四种引用类型在垃圾回收时的处理方式。简而言之,一个对象如果通过GC Root可达,那么强引用不会被回收,软引用在内存不足时会被回收,弱引用则在对象第一次GC时就会被回收。而当GC Root不可达时,不论何种引用类型,都会被回收。虚引用则比较特殊,它几乎等同于没有引用,不会影响对象的生命周期,但可以在对象被垃圾收集器回收时收到系统通知。

接下来,我们通过具体的案例来探讨这四种引用在面对GC时的表现以及它们的常见用途。设置JVM参数以便更好地观察GC行为:-Xms20M -Xmx20M -Xmn10M -verbose:gc -XX:+PrintGCDetails。

强引用

这是我们日常编程中最常接触的引用类型。只要GC发生时对象通过GC Root可达,它就不会被回收。如果JVM内存不足,将会抛出OOM(OutOfMemoryError)。例如,下面的代码片段在内存不足时会抛出OutOfMemoryError:

```java

public static void main(String[] args) {

List list = new LinkedList<>();

for (int i = 0; i < 21; i++) {

list.add(new byte[1024 1024]); // 这里会创建大量的强引用对象

}

}

```

软引用

软引用在GC时,如果对象通过GC Root可达且内存足够,则不会被回收;但当内存不足时,会被垃圾收集器清理。将上述例子中的普通对象改为软引用包装,就可以避免OOM的发生。通过改造程序打印GC前后的差异,我们可以观察到软引用对象在GC后的变化情况。当堆内存最大设置为20M时,调整循环次数可以发现,GC后部分软引用对象被回收,但通过-verbose:gc -XX:+PrintGCDetails参数可以看到JVM仍然进行了几次GC尝试。

引子:从主方法开始探索

在一个静谧的代码世界中,我们从main方法出发,揭开软引用、弱引用、虚引用的神秘面纱。

软引用:缓存的持久守护者

当我们创建一个SoftReference对象并将其添加到列表中时,这个对象就像一个守护者,守护着我们的缓存数据。只要对象未被GC回收,我们就能通过SoftReference获取到数据。一旦GC发生,软引用指向的对象就会被温柔地释放。软引用特别适合那些希望缓存持续时间稍长一些的场景。

弱引用:短暂的临时庇护所

与软引用不同,弱引用下的对象只要经历一次GC就会被回收,生存时间相对短暂。它们犹如临时的庇护所,为数据提供短暂的缓存空间。在Web应用中,弱引用常被用于管理短暂的生命周期数据。

弱引用的代表——WeakHashMap

WeakHashMap利用弱引用来管理键值对。当键对象成为GC的目标时,对应的键值对将从Map中优雅地删除。在Tomcat的ConcurrentCache中,结合WeakHashMap和ConcurrentHashMap实现了一个线程安全的缓存机制。源码精简且注释详尽,仅短短59行便勾勒出强大的功能。

ThreadLocal中的弱引用智慧

ThreadLocal中的静态内部类ThreadLocalMap利用弱引用机制来判断ThreadLocal对象是否失效。一旦对象失效,其所占用的Map数据也将失去价值并被清除。这种设计确保了ThreadLocalMap始终保持高效状态。

虚引用的独特设计

虚引用与其他引用类型在设计上有所不同,它不影响GC过程,而是在对象被GC回收时发送系统通知。如何被通知呢?虚引用需要配合ReferenceQueue。当GC准备回收一个对象时,若发现它还有虚引用,就会在回收前将这个虚引用加入到与之关联的ReferenceQueue中。NIO利用虚引用来管理内存,通过一种通知机制清理在Java堆外申请的内存。DirectBuffer内的Cleaner组件,作为PhantomReference的子类,会在DirectBuffer对象被回收后收到通知。

结尾:深入了解引用类型,发掘更多应用场景

---

在Reference类的方法中,有一个重要的方法由ReferenceHandler调用,名为tryHandlePending()。当这个方法被触发时,它的首要任务是处理pending状态。如果检测到pending状态不为空,那就意味着DirectBuffer已经被回收。此刻,我们可以调用Cleaner的clean()方法进行资源回收。

这个处理流程的代码被巧妙地嵌入在Reference类中。对于那些对源码充满好奇心的同学们,不妨深入探究一下这个方法,感受其内部逻辑的魅力。

Java中的四种引用各有特色,区分清楚它们的用途对于程序员来说十分重要。众所周知的是强引用,而虚引用在实际开发中并不常用。相比之下,软引用和弱引用的主要区别在于它们的回收时机:软引用在GC时仅在内存不足时才会被回收,而弱引用只要触发GC就会被立即回收。

关于作者:

欢迎来到yasinshaw.com,这里是由笔名Yasin的程序员精心打造的个人网站。作为一名程序员,我深知技术的深度、态度和温度的重要性,并始终致力于分享我的知识和经验。在这里,你可以感受到我对编程的热爱与追求,一起探讨技术的无限可能。

文章从网络整理,文章内容不代表本站观点,转账请注明【蓑衣网】

本文链接:https://www.baoguzi.com/69558.html

一次性把Java的四种引用说清楚! | 分享给朋友: