一、java.lang.ref
java.lang.ref包规范(参看JDK API中所述内容,本文就不贴出来了)有几个点很重要。
1. 这个包中的类使得应用程序可以与JVM垃圾收集器进行一些交互;
2. 引用的可达性逐渐降低:强引用、弱引用、软引用、虚引用;
3. 自动清除引用:只有弱引用和软引用会自动清除引用(这表明虚引用不会自动清除),清除的时机是将引用注册到引用队列之前(只有当内存不够用,软引用才会被回收;无论内存够不够用,只要发生垃圾回收,弱引用都会被回收);
4. 虚引用不会自动清除引用,当一个虚引用被认为是一定会被垃圾回收器回收的时候,这个虚引用才会被注册到引用队列,而不会像软引用和弱引用必须等到垃圾回收器回收之后才会被注册到引用队列;
解决:
1. 验证弱引用和软引用的自动清除;
2. 侧面说明虚引用注册时机——什么是一定会被垃圾回收器回收;
二、验证准备
1. 验证方法介绍
a. 选择java.lang.ref.Reference中的方法isEnqueued来验证当前引用是否已经进入引用队列;清晰的逻辑还需要知道一个方法,即enqueue,这个方法会将当前引用放入队列并修改当前引用的队列为ReferenceQueueENQUEUED,整个原理很简单,剩余的代码就不贴出来了。
b. 当一个引用已经进入队列,则可以用调用java.lang.ref.ReferenceQueue中的方法poll来将引用出队列,特别注意的是,当一个引用出队列的时候,他自身关联的引用队列会再一次被修改(上一次是进入队列的时候),整个思想可以这么来说,“不惹毛我的时候我们是朋友(引用和引用队列是依赖关系),惹毛了我就搞你(进入队列时候第一次被修改,引用不再依赖于引用队列),搞完你了谁也不认识谁(出队列的时候第二次修改)”,其实引用在出列的时候就没有存在的必要了,但是引用队列可能还被其他引用依赖,因此出队列的引用要显式的Reference=null。
2. 验证类介绍
准备一个类,不重写finalize方法、重写finalize方法、重写的finalize方法产生this逃逸。
3. 知识铺垫
本文所涉及的关于垃圾回收的小知识补充。
在《深入java虚拟机(周志明著)》一书中垃圾收集算法“可达性分析”(用来陈述什么样的对象会被垃圾回收),当一个对象被认为是不可达(可达、可恢复、不可达三种状态)状态时,该对象就会被垃圾回收。JVM判断一个对象为不可达,一是该对象不存在强引用,二是是否有必要调用该对象的finalize方法(一个对象生命周期中只会被调用一次,千万记住,该方法只会被调用一次),因为如果重写了Object中的finalize方法,重写逻辑中有可能会使得该对象变成可达状态,那么该对象将不会进行来及回收。
是否有必要调用该对象的finalize方法,当该对象的类没有重写Object的finalize方法或该对象的finalize方法已经被调用过,则JVM认为不必调用,该对象会立即认为是不可达的;稍微复杂一点的情况是,一个对象的类重写了Object的finalize方法且尚未被调用过,则该对象会被放入一个叫做F-QUEUE的低优先级队列中,等待被执行finalize方法,如果finalize方法被执行之后,该对象依旧不可达,则该对象进入不可达状态,等待被垃圾回收,如果finalize方法被执行后该对象变为可达状态(finalize方法this逸出问题),则该对象不会被垃圾回收,对象变为可达状态。
三、PhantomReference
1. 自动清除
重不重写finalize方法都可以,只要不发生finalize逃逸,则内存一定会被回收,但是千万要注意的是,如果用String类来做验证类,可能得不到你想要的结果,因为JVM内建字符串池机制的存在会导致字符串池强引用的存在,因此不会被垃圾回收,建议使用其他类做验证类。
String做测试和自定义类做测试的结果,如下图所示,String对象没有被垃圾回收,因此不会被加入队列。
2. 虚引用
a. 不重写finalize方法
2. 重写finalize方法
3. 当finalize方法发生this逃逸
四、这才是虚引用
不像软引用、弱引用会自动回收内存,虚引用的存在(虽然内存还是会被回收)更倾向于发送通知,当一个对象确定会被回收之后(此时虚引用中的引用对象并不能确定是否已经被回收内存了,而软引用和弱引用一定是被回收内存了的),就会向应用程序发送一个通知(进入队列和出队列),“我要被清理了,你们是否要做些什么事情呢?”。
所以,虚引用用来做对象清理工作,比finalize方法再好不过了,不会导致垃圾回收器额外做工作,配合Reference阻塞方法remove就能更及时做清理工作。
版权声明:本文为博主原创文章,未经博主允许不得转载。