在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事就是要确定这些对象是否存活
1.引用计数算法(Reference Counting)
给对象添加一个引用计数器,每当有一个地方引用它时,计数器就会加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
引用计数器的实现简单,判定效率很高。大多数情况下是极好的算法,but 很难解决对象之间相互循环引用的问题。
执行以上代码后,GC日志文件中显示对象被回收,即说明虚拟机并不是通过引用计数算法判断对象是否存活的。
2.可达性分析算法(Reachability Analysis)
在主流的商用程序语言(java、C#、古老的Lisp)的主流实现中,都是通过可达性分析来判定对象是否存活的。
基本思路:通过一系列被称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径被称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(即:GC Roots到这个对象不可达)时,证明此对象不可用。
Java语言中,可以作为GC Roots的对象包括:
2.1虚拟机栈(栈帧中的本地变量表)中引用的对象
2.2方法区中类静态属性引用的对象
2.3方法区中常量引用的对象
2.4本地方法栈JNI(即:Native方法)引用的对象
3.引用
以上两种算法判定对象是否存活都与“引用”有关。JDK1.2以前对引用的定义:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。
JDK1.2以后,将引用分为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),强度依次降低。
强引用:类似“Object obj=new Object()”,只要强引用还在,垃圾收集器就永远不会回收掉被引用的对象
软引用:JDK1.2以后,提供SoftReference类实现软引用,用来描述一些有用但并非必需的对象。对于这类对象,在系统将要发生内存溢出异常以前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收之后还没有足够内存,才会抛出异常。
弱引用:JDK1.2以后,提供了WeakReference类来实现弱引用,用来描述非必需对象,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当下一次垃圾收集进行时,无论内存是否足够,都会回收这个对象。
虚引用:(幽灵引用/幻影引用),JDK1.2以后,提供了PhantomReference类来实现虚引用,对象是否有虚引用,对该对象的生存时间完全没有影响,也无法通过虚引用来取得一个对象实例。唯一目的在于当这个对象被垃圾收集器回收是收到一个系统通知。
4.生存还是死亡
当可达性分析算法,判定一个不可达对象时,该对象也不是非死不可,即被判“缓刑”阶段,然后两次标记过程:可达性分析之后发现对象与GC Roots相连接的引用链,那么对象就被第一次标记并进行一次筛选,筛选的条件是该对象有没有必要执行finalize()方法,当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,都被视为“没有必要执行”。
执行finalize()方法时,会先将这个对象放置在一个F-Queue队列之中,并在稍后由虚拟机自动建立一个低优先级的Finalize线程去执行。只是触发该方法,但不保证等不等方法结束。要是在finalize()方法中,该对象只要重新与引用链上的任何一个对象建立连接即可(比如把自己赋值给某个类变量或者对象的成员变量),这样的话这个对象就从垃圾收集器的虎口逃生了。