《Effective Java》避免使用finalize()方法

首先说一下finalize()方法用来做什么,这是一个Object类的方法,也就是说所有类都会继承这个finalize()方法,这个方法默认实现为空,这个方法被用于在类对象被GC之前做一些收尾工作,但是被执行是有条件的,类重写的finalize()方法必须不为空。

首先JVM会先判断这个类是否重写了finalize()方法,并且这个方法不为空,如果满足这个条件会被标记。

在java.lang.ref包中的Finalizer类,如果一个类不再与引用链相连结,这意味着这个对象会被垃圾回收(关于这里不理解的可以自行搜索GCRoots),在回收之前会判断这个类是否已经重写了finalize()方法并且这个方法不为空,是否从未执行过finalize()方法(不理解可以搜索垃圾回收对象的二次拯救),如果同时满足这两个条件,才会考虑执行finalize()方法,并且不是直接执行的,下面简单介绍下具体流程。
如果同时满足以上两个条件的话,JVM会调用这个register()方法,这里创建Finalizer类的实例。

我们知道创建一个类的实例后调用顺序是这样的:
- 父类静态代码块
- 子类静态代码块
- 父类代码块
- 父类构造方法
- 子类代码块
- 子类构造方法

这个类的父类不在我们的讨论范围,我们可以直接看这个类的静态代码块。

这里主要是创建了一个优先级为3的守护线程并启动了它。

我们再来看这个类的构造方法。

首先这个构造方法时private,我们是无法继承这个类的,接下来看一下add()方法。

作用是将当前引用加入一个双向链表中,在这时我们可以发现宏观上这是一个由Finalizer类对象组成的一个队列,我们继续来看这个守护线程类的run()方法中的for循环中的代码,首先将队列中的引用移除,然后调用了runFinalizer()方法,我们来看看这个方法做了些什么?

首先,在同步代码块中判断是否已经执行过finalize()方法了,如果已经执行过,就直接移出队列,等待GC。如果没有执行过,就直接执行finalize()方法,这里是JVM来负责执行,最后调用父类的父类Refrence的clear()方法,将refrence设置为null,这样就可以等待垃圾回收了。

具体步骤并不仅仅是这些,这里只是分析finalize()的缺陷。读者有兴趣的话,可以看一下Refrence类中的队列的实现。

总结:finalize()在一些时候不保证会被执行,前面提到,因为是一个优先级为3的守护线程执行,所以在大量线程占有资源的时候,这个线程会很难获得CPU资源去执行,这个线程会等待执行,在等待的这段时间,有可能经历数次年轻代GC,大量对象晋升到老年代,有可能引发full gc甚至OOM。

你可能感兴趣的:(Effective,Java,Effective,Java读书笔记)