PS:文章来源《深入理解java虚拟机第三版 3.2对象已死?》
1.引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。但是在java领域中并没有选用引用计数算法来管理内存,主要原因是这个看似简单的算法有很多例外情况要考虑,必须要配合大量额外处理才能保证正确的工作,譬如单纯的引用计数就很难解决对象之间的相互循环引用的问题。
2.可达性分析算法
比较主流的成语语言都采用可达性分析算法,该算法的基本思路是通过一系列的“GC Roots”的根对象作为起始节点集,从节点开始向下搜索,搜索过程所走过的路径为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
在java技术体系里,固定可作为GC Roots的对象包括以下几种:
①在虚拟机栈(栈帧中的局部变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用的参数、局部变量、临时变量等。
②在方法区中静态属性引用的对象,譬如java类的引用类型静态变量。
③在方法区中常量引用的对象,譬如字符串常量池里面的引用。
④本地方法栈中引用的对象。
⑤虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(如NullPointException、OutOfMemoryError)等,还有系统类加载器。
⑥所有被同步锁(synchronized关键字)持有的对象。
⑦反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
⑧除了以上固定的GC Roots集合以外,根据用户所选的垃圾收集器以及当前回收的内存区域,还可以有其他对象“临时性”加入共同构成GC Roots集合。例如只对某一区域进行收集,该区域存在跨带引用的对象,则需要将这些关联区域的对象一并加入到GC Roots集合中才能保证可达性分析的正确性。
3.引用分类
①强引用是最传统的“引用”的定义,是指在程序代码中普遍村子的引用赋值,如“Object obj = new Object()”。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
②软引用用来描述一些还有用,但非必须的对象。在系统将要发生内存溢出前,会把这些对象列进回范围之中进行第二次回收,如果回收后还没有足够的内存,才会抛出内存溢出异常。在JDK1.2版本之后提供了SoftReference类来实现。
③弱引用与软引用类似,区别就是被弱引用管理的对象只能生存到下一次垃圾收集发生为止。在JDK1.2版本之后提供了WeakReference类来实现。
④虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。对象有无虚引用不对其生存时间构成影响,也无法通过该引用获取对象实例。为一个对象设置虚引用的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。JDK1.2版本之后提供了PhantomReference类来实现。
4.finalize
如果一个对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将被第一次标记,随后进行一次筛选,筛选条件是对象是否有必要执行finalize()方法。假如对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机认定这两种情况为没必要执行finalize()方法。
若有必要执行finalize()方法,则对象将被放置到一个F-Queue队列,由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们的finalize()方法。对象可通过调用finalize()方法时重新与引用链上的任何一个对象建立关联即可不被回收。finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,已被官方明确申明不推荐使用的语法。