Effective Java 第三版读书笔记——条款8:避免使用 Finalizer 和 Cleaner 机制

Finalizer 机制通常是不可预知的、危险的、不必要的。它们的使用会导致不稳定的行为,糟糕的性能和移植性问题。从 Java 9 开始,Finalizer 机制已被弃用,但仍被 Java 类库所使用。 Java 9 中 Cleaner 机制代替了 Finalizer 机制。 Cleaner 机制不如 Finalizer 机制那样危险,但仍然是不可预测、运行缓慢并且通常是不必要的。

Finalizer 和 Cleaner 机制的缺点:

  • 不能保证他们能够及时执行。 从一个对象变得无法访问到 Finalizer 和 Cleaner 机制开始运行的这段时间是任意长的。 这意味着你永远不应该用 Finalizer 和 Cleaner 机制做任何时间敏感(time-critical)的事情。Java 规范不能保证 Finalizer 和 Cleaner 机制能及时运行;它甚至不能保证它们是否会运行。

  • Finalizer 机制的另一个问题是在执行 Finalizer 机制过程中,未捕获的异常会被忽略,并且该对象的 Finalizer 机制也会终止。未捕获的异常会使其他对象陷入一种损坏的状态(corrupt state)。如果另一个线程试图使用这样一个损坏的对象,可能会导致任意不确定的行为。通常情况下,未捕获的异常将终止线程并打印堆栈跟踪( stacktrace),但如果发生在Finalizer机制中,这些异常甚至不会打印警告信息。Cleaner 机制没有这个问题,因为使用 Cleaner 机制的类库可以控制其线程。

  • 严重的性能损失。 通常来说,使用 finalizer 机制创建和销毁对象要慢大约 50 倍。这主要是因为 finalizer 机制会阻碍有效的垃圾收集。

  • 严重的安全问题:它们会打开你的类来进行 finalizer 机制攻击。

Finalizer 和 Cleaner 机制的两个合法用途:

  • 作为一个安全网(safety net),以防资源的拥有者忘记调用 close 方法。虽然不能保证 Finalizer 和 Cleaner 机制会及时运行(或者是否运行),但是将资源晚一点释放也要好过永远不释放。

  • 第二种合理使用Cleaner机制的方法与本地对等类(native peers)有关。本地对等类是一个由普通对象委托的本地(非 Java)对象。由于本地对等类不是普通的 Java 对象,所以垃圾收集器并不知道它,当它的 Java 对等对象被回收时,本地对等类也不会被回收。假设性能是可以接受的,并且本地对等类没有持有关键的资源,那么 Finalizer 和 Cleaner 机制可能是这项任务的合适的工具。

Cleaner 机制使用起来有点棘手。下面是演示该功能的一个简单的 Room 类。假设 Room 对象必须在被回收前清理干净。Room 类实现 AutoCloseable 接口;它的自动清理安全网使用的是 Cleaner 机制,这仅仅是一个实现细节。与 Finalizer 机制不同,Cleaner 机制不会污染一个类的公开API:

// An autocloseable class using a cleaner as a safety net
public class Room implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();

    // Resource that requires cleaning. Must not refer to Room!
    private static class State implements Runnable {
        int numJunkPiles; // Number of junk piles in this room

        State(int numJunkPiles) {
            this.numJunkPiles = numJunkPiles;
        }

        // Invoked by close method or cleaner
        @Override
        public void run() {
            System.out.println("Cleaning room");
            numJunkPiles = 0;
        }
    }

    // The state of this room, shared with our cleanable
    private final State state;

    // Our cleanable. Cleans the room when it’s eligible for gc
    private final Cleaner.Cleanable cleanable;

    public Room(int numJunkPiles) {
        state = new State(numJunkPiles);
        cleanable = cleaner.register(this, state);
    }

    @Override
    public void close() {
        cleanable.clean();
    }
}
posted @ 2018-11-17 19:22  LeeFire  阅读(355)  评论(0编辑  收藏  举报