java中Reference学习
谈到Reference想到了什么
Reference提供了一种与jvm gc交互的一种方式,提到Reference,脑中应该浮现一些名词,gc、ReferenceQueue、SoftReference、WeakReference、PhantomReference、FinalReference以及最常见的强引用。我认为当一个小白开始学习Reference的时候应该想一个问题,强引用不够好么,为什么我还需要弱引用、软引用等等这些东西。
为什么我们需要它们
应用程序经常会遇到这样一种情况,有些类天然不适合被拓展,这些类可能被标记为了final,或者可能情况更加复杂些,它是被一个工厂方法返回,你压根就不知道它具体的实现类,你仅仅知道它是某个接口,然而你想要拓展他们,因此你可能使用了一个map,key是他们的示例,value是你想使用的一些拓展属性,比如一个名字,Ethan Nicholas‘s Blog举了个例子:
Widget类就不能被拓展,而我们希望给这些对象一些序列号widgetSerialNumber,因此我们可能会这样:
serialNumberMap.put(widget, widgetSerialNumber);
看起来一些很美好,但是这个map拥有着widget的强引用,当这个类不再被需要的时候,我们可能需要手动删除,否则它不可能被gc。看起来问题回到了没有gc的时代(C++手动管理内存),我们不得不时刻考虑着这块内存的所有权,当应该释放的时候把它释放掉,这太不java了。
因此有些美好的特性就出来帮你解决这个问题,
WeakReference weakWidget = new WeakReference(widget);
通过一层弱引用封装,jvm在做可达性分析的时候就可以再正确的时机将他们清理调。因此我们再代码中就可能会偶然发现weakWidget.get()返回null,说明已经被gc回收了。
Reference queues
没个Reference对象在构建的时候可以传递一个ReferenceQueue对象示例,这个对象是我们面对gc时候的一把钥匙,从名字就可以看出它就是一个队列(翻看源代码我觉得叫栈更合适,因为总是在head插入,head出删除),准确的说其实就是个链表,链表的元素就是Reference对象,可以执行一些基本的入队、删除操作,注意到这个remove是阻塞的,而poll并不阻塞而是立即返回null假如队列没有元素。
为什么说它是一把钥匙,因为一般在使用的时候我们会构造一个ReferenceQueue示例对象,然后在后面构造弱引用等类型时候传入,这样当他们被gc回收的时候,remove方法就会返回被gc的示例,我们就可以做一些清理工作,这个模型非常像事件模型,当然对于weakReference而言,入队并不一定会被gc,因为有finalize方法存在,可能会复活一些对象,但是对于PhantomReference而言,入队了这块内存一定被gc了,因此PhantomReference可以作为一个gc时间的listener(有get到一个小trick),但是使用起来其实并不是很方便。
引用的强度
很明显,从名字就可以看出来,引用强度从大到小: strong > soft > weak > phantom。
其中soft和weak的引用其实很像,区别在于weak引用一旦发生gc就会将它回收,而soft引用会尽可能撑到没有内存可用的时候,被回收。
Phantom引用更以上都不太一样,它几乎不能够持有一个对象,它get()总是返回null,唯一的作用就是从ReferenceQueue中取出来使用,当取出来时候我们就知道它已经被回收了,我们可以很放心做一些该有的回收动作。
当我在学习的时候,我一直想不明白,他们一被gc就回收,这用起来多不放心,但是当我接触到几个demo代码的时候我就明白了,他们从来不是在单兵作战,基本上他们的使用都会和强引用配合。
因此对象可达性分析就很重要了,实际上以上的情况准确说是weak可达性或者soft可达性或者phantom可达性。
下面引用一下java学习教程之Reference详解中的图示:
那么 垃圾回收时会依据两个原则来判断对象的可达性:
- 单一路径中,以最弱的引用为准
- 多路径中,以最强的引用为准。
有这两句话所有的解答就出来了。
实际的一些例子后面补,先挖个坑。
原文地址:https://www.cnblogs.com/snailLx/p/9165265.html