引用计数法
方式:每个对象有一个引用计数属性,新增一个引用时计数加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。
所谓自救就是把自己与引用链上的一个对象关联起来。