【Java】Java的四种引用

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

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

参考自此博客

强引用

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

比如下面就是一个强引用

Object o = new Object();    // 强引用

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

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

o = null;       // 帮助GC回收这个对象

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

比如我们下面的例子

public void function(){
    Object o = new Object();
}

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

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

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

private transient Object[] elementData;
...
public void clear() {
        modCount++;
        // Let gc do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
}

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

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

软引用

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

 Integer num = new Integer(1);                                          // 强引用
 SoftReference<Integer> softRef = new SoftReference<Integer>(num);      // 软引用

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

  1. 从缓存中重新取出

  2. 重新进行请求加载

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

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

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

Browser prev = new Browser();               
SoftReference ref = new SoftReference(prev);    
if(ref.get()!=null){ 
    rev = (Browser) ref.get();          
}else{
    prev = new Browser();               
    ref = new SoftReference(prev);       
}

弱引用

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

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

String str=new String("abc");    
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
str=null; 

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

虚引用

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

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

总结

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

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

N0tExpectErr0r

一名热爱代码的 Android 开发者

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>