【Java】Java的四种引用


众所周知,Java是有四种引用的,分别是:强引用、弱引用、软引用、虚引用。

为了学习这些引用之间的区别和联系,写下这篇博客。

参考自此博客

强引用

我们平时用到的最普遍的引用,就是强引用。如果一个对象具有强引用,GC就不会回收它。

比如下面就是一个强引用

当我们内存不足时,Java虚拟机宁愿抛出OutOfMemoryError异常也不愿意回收具有强引用的对象。

因此,当我们不使用一个对象时,应该用如下方式来弱化引用,帮助GC回收

当我们显式地设置o为null,或者超出了对象的生命周期范围,GC会认为该对象不存在引用,这时就可以回收这个对象。但要注意的是,不一定是马上回收!具体何时回收取决于GC的算法。

比如我们下面的例子

我们在方法中有一个强引用,它保存在栈中。而真正new出来的对象则保存在堆中。当方法结束,强引用的生命周期结束,此时引用不再存在,这个对象就会被回收。

当o为全局变量时,我们就需要在使用结束后将它置为null,来辅助GC对其进行回收。

比如ArrayList的源码中,就有一个clear函数来辅助GC回收它内部的数据

可以看到,它内部执行的操作就是把所有元素全部置为null,而不是仅仅把elementData置为null。如果仅仅把elementData置为null,强引用会依然存在。

当面对数组元素时,采用这样的方法可以及时释放它的内存。

软引用

采用软引用,当内存空间足够的时候,GC不会回收它。而内存空间不足时,GC就会回收这些对象。只要不被回收,就可以继续使用。

软引用在实际的使用场景中十分重要,比如我们浏览器的后退按钮。当我们按下后退键时,加载前一个网页有两种策略

  1. 从缓存中重新取出

  2. 重新进行请求加载

如果我们仅仅采用第一种策略,会造成大量的内存浪费,甚至内存溢出。

如果我们仅仅采用第二种策略,会浪费网络资源,并且加载缓慢。

此时,我们就可以使用软引用来存放。

弱引用

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

在GC线程扫描它所的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

如果这个对象偶尔使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么应该用 Weak Reference 来存储此对象。

虚引用

虚引用和其他几种引用都不同,它是形同虚设的,并不会决定对象的声明周期。如果一个对象仅仅只有虚引用,和它没有引用是一样的,随时会被GC回收。

使用虚引用主要用于追踪对象被回收的过程。

总结

可以用下表来总结本篇文章

引用类型 被垃圾回收时间 用途 生存时间
强引用 从来不会 对象的一般状态 JVM停止运行时终止
软引用 在内存不足时 对象缓存 内存不足时终止
弱引用 在垃圾回收时 对象缓存 gc运行后终止
虚引用 Unknown Unknown Unknown

Android Developer in GDUT