引用计数法

方式:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。
优点:实现简单,效率高。
缺点:无法解决循环引用。

可达性分析法

方式:从一系列被称为GC ROOT的对象开始,向下搜索,搜索走过的路径称为引用链,当一个对象到GC ROOT之间没有引用链,说明这个对象不可用。

GC ROOT对象:

  • 虚拟机栈中引用的对象
  • 方法区内类的静态属性引用的对象
  • 方法区常量引用的对象
  • 本地方法栈中引用的对象

finalize()

解释1

finilized函数是遗留产出,和C++的析构函数有本质区别,调用了finilized函数对象不一定会被回收,finilized函数也不是每次必会被调用。一般回收对象会调用这个函数,但我们不必重写这个函数因为Object中已经实现了一个空的finilized。

protected void finalize() throws Throwable { }

经过根搜索算法的可达性分析某个对象没有和GC Roots的直接或间接联系(不可达),并不意味着该对象将被回收,还需要经过两次标记和筛选工作,才能决定是否可以回收对象。

第一次:判断是否有finalize方法或者执行过finalize,如果没有finalize方法或者已经执行过finalize方法,不需要进行筛选则可以回收,否则需要进行筛选进行第二次标记和筛选。

第二次:执行对象的finalize方法,执行完成或者执行过程中判断对象是否和GC Roots是否有直接或者间接联系,如果依然没有联系则把对象放入回收列表等待回收,否则对象复活。

finilized函数执行的大致过程:

当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法或者是否执行过了finalize方法,若未覆盖或者已经执行,则直接将其放入回收列表等待回收。

否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法,不一定等待finalize方法执行完毕后才进行判断(因为如果等待finalize执行完毕,可能具有一定的延迟,导致F-Queue队列阻塞),则执行finalize方法过程中,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。

Java中的finalize的调用具有不确定性,就是说何时调用(调用时机)是不确定的,JVM也不保证一定会被调用。

子类可以实现这个方法做一些回收资源的操作。

可以主动调用finilized函数吗?

finalize()可以主动调用, 但不建议调用, 由于gc也会调用可能会发生某种异常导致资源释放出现问题。

解释2

当一个对象被判定为不可达对象后,也并不是非死不可。

在通过可达性分析算法判断没有引用链使之与GC ROOT相连,会判断该对象是否有必要执行finalize方法:

假如重写了finalize,并且未调用过,则说明有必要执行。

判断有必要执行finalize的对象,会被放入一个队列,有jvm建立的低优先级的Finalizer线程去执行。

当在finalize中自救成功的对象,就会在第二次标记时移除即将回收的集合。

自救失败的就会被回收,不会再执行finalize。

所谓自救就是把自己与引用链上的一个对象关联起来。

评论