Java杂谈2——引用与跟搜索算法

Java中的引用

  Java“引用”的概念源于C++,原本的定义相当有限:一个引用(Reference)代表的内存通常用于指向另一块内存区域的起始地址。通过引用类型保存的起始地址,可以找到这个引用所指向的对象实例和在方法区中的对象类型数据。

  区别于传统的c/c++语言,Java的对象的销毁完全由垃圾回收器管理,核心问题就是何时应该回收对象?

  Java的策略是:回收那些再也不会被任何引用指向的对象,我们将实例化对象的是否被引用的特性称之为可达性

  其实,这种策略并不涵盖所有的使用情景。试想,我们希望创建一些缓存对象用于存储临时的中间计算结果,当内存充裕时将它们会保留在内存中;当内存非常紧张时,即使优先回收这些对象对程序的执行正确性并不会有影响。此时,传统的Java引用就显得束手无策了。自JDK1.2后,Java引用的概念得到了新的扩展,将引用划分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。

  强引用 > 软引用 > 弱引用 > 虚引用

  强引用:Java代码中默认的引用类型就是强引用,除非实例化对象不可达,否则不会被回收。例子如下:

1 Object objRef = new Object(); //objRef是一个强引用

  软引用:即使实例化对象可达,软引用的对象还是会发生内存溢出之前(Java堆内存不够用时)被垃圾回收器回收,例子如下:

//上接之前的代码,创建一个软引用
SoftReference objSoftRef = new SoftReference(objRef);

Object objRef2 = (Object)objSoftRef.get(); //如果此时软引用还未被回收,可以通过这段代码重新或得到实例化对象的强引用,否则会返回null

  弱引用:比软引用更弱,在下一次垃圾回收器收集到弱引用后对象会被回收,具体用法与软引用一致,弱引用类类名为WeakReference。

  虚引用:这个引用很特殊,其存在不影响对应的实例化对象的生命周期,也不能够通过get方法再获取到对象的强引用。与一个特殊的引用队列(ReferenceQueue)配合使用可以用于监听实例化对象的回收事件。例子如下:

//上接之前代码,创建一个引用队列
/************************************************
** 解释一下引用队列:
** 在一个实例化对象被垃圾回收器回收之前,与这个对象相关的引用对象(Java.lang.refReference类型)都会被加入到与之相关的引用队列中
*************************************************/
ReferenceQueue queue = new ReferenceQueue ();

//创建一个虚引用,同时将这个虚引用与队列queue相关联
PhantomReference objPhaRef = new PhantomReference(obj,queue);

assertNull(queue.poll());

obj = null;

//obj对象在被回收之前,objPhaRef 对象就会被加入到queue队列中
assertNull(queue.poll());

 根搜索算法

  垃圾回收器在判定一个对象是否可以被回收时采用的是根搜索算法,基本思想:从已知的GC Roots对象开始遍历向下搜索,找到所有的可达实例化对象,回收所有不可达的实例化对象。GC Roots对象包含如下:

  虚拟机栈中的引用对象

  方法区中的静态引用对象

  常量池中的常量引用对象

  本地方法栈中的引用对象

 

  那么,当一个实例化对象在被确定不可达之后,会被马上“杀死”吗?

  答案是否定的,其中的例外小技巧就是在finalize()函数。为了保证c++程序员能够接受,java提供了类似于析构函数的finalize,垃圾回收器保证一个对象在被真正销毁之前必定调用过一次finalize。那么,我们就可以在这个finalize函数中再次将本对象赋值给一个外部引用,试图在真正销毁之前挽救对象。(PS:但是需要注意的是,类似于表达finalize函数并不是java中的虚构函数,它并不保证是在对象真正销毁之前被调用的最后一个函数,它只保证一个对象在被销毁之前必定会被调用过一次)。

原文地址:https://www.cnblogs.com/yahokuma/p/3662650.html