(3) 虚拟机中对象的生死判断

时间:2022-12-27 18:22:20

Java堆中存在的对象实例,垃圾回收器再进行回收前,首先要判断对象是存货还是死亡。

1.引用计数算法

所谓的引用计数法,就是给的对象添加一个引用计数器,当对象被引用时,计数值加一,引用失效时,计数值减一。计数器的值为0时就是对象没有被引用。

引用计数法简单高效,但是难以解决对象之间的循环引用问题,因此大多数Java虚拟机都没有采用这种算法

public class test{
    public Object instance = null;
    test A = new test();
    test B = new test();
    A.instance = B;
    B.instance = A;
    A = null;
    B = null;
    
    System.gc();
}

上述程序中,A和B互相引用,但除此之外,这两个对象再无任何引用,已经不能再访问。但是因为他们互相引用着对方,所以计数值不为0,所以引用计数器无法通知GC来对这两个对象进行回收。

2.可达性分析算法

在主流的程序语言中的实现中,一般通过可达性分析(Reachablitity Analysis)来判定对象是否存活。可达性分析的基本思想是是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索走过的路径成为引用链,当一个对象到起始点没有任何引用链相连时,也就是起始点到这个对象不可达,则可以证明这个对象是不可用的,就可以判定为可以被回收的对象,等待GC进行回收

(3) 虚拟机中对象的生死判断


如上图所示,左侧的对象可达GC Roots 因此仍然存活, 右侧的就判定为可以回收的对象

3.Java扩充后的引用概念

JDK1.2后,Java对于引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种,引用强度依次逐渐降低。

强引用是普遍存在的,类似与new一个对象这种引用,只要强引用还在,GC永远不会对其回收

软引用用来描述还有用但非必须的对象。软引用关联的对象,在系统快要发生内存溢出时,会对其进行二次回收,二次回收后还没有足够的内存才会报内存溢出异常。

弱引用比软引用的强度还要弱一些,GC工作的时候,无论内存是否足够,都会回收被弱引用关联的对象

虚引用是最弱的引用形式,一个对象是否有弱引用不会对其生存时间构成影响,也无法通过虚引用得到这个对象,被虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知。

4.finalize()方法

在可达性分析中不可达的对象,真正要进行回收,还要经过两次标记过程。

第一次标记筛选的条件是此对象是否有必要执行finalize()方法,对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,这两种情况下finalize()方法都没必要执行;

如果有必要执行,这个对象会放在一个叫F-Queue的对列中,稍后由一个虚拟机自动建立优先级较低的Finalizer线程去执行,稍后GC会对F-Queue中的对象进行第二次标记,如果在这里调用finalize()方法里,对象重新建立了与引用链上的引用,对象就会移出被即将回收的集合。但是一个对象的finalize()方法只能被执行一次,在第二次回收的的时候,finalize()方法不会被再次执行。

5.回收方法区

方法区或者HotSpot虚拟机中的永久代也是要进行来及回收的,即使回收效率比较低。回收的内容只要包括两个部分:废弃的常量和无用的类。

废弃的常量指的是没有被引用的常量,与回收Java堆中的对象非常类似。

判定一个类是否为无用的类条件比较苛刻,主要要满足三个条件:(1)类的所有实例都已经被回收(2)加载类的ClassLoader也已经被回收(3)该类对应的java.lang.Class对象没有被引用,无法在任何地方通过反射访问该类的方法。