07-避免使用终结方法

本条目大意

尽量避免在类中使用终结(finalize)方法,在里面写一些释放类中资源的语句。


为什么要避免使用 finalize方法?

1、java语言规范不仅不保证 finalize方法会被及时地执行,而且根本不保证他们会被执行。
2、System.gc 和 System.runFinalization 这两个方法只是增加了finalizer 方法被执行的机会。
3、唯一能保证 finalize 方法被执行的方法有两个,System.runFinalizersOnExit 和 Runtime.runFinalizersOnExit ,但是这两个方法已经被废弃。
4、覆盖并使用终结方法会有严重的性能损失。
5、及时地执行终结方法是垃圾回收算法的一个主要功能,但是在不同的JVM实现中会大相庭径,使用终结方法可能会丧失平台无关性。


如果类的对象中封装的资源确实需要进行释放,我们应该怎么做呢?

一般来说,需要释放资源的有线程或者文件还有涉及到本地资源的对象。

我们不去覆盖 finalize 方法,而是自己提供一个显式的终止资源的方法。比如 java.io.FileInputStream 的close 方法。当使用完这个FileInputStream对象时,显式调用close() 来回收资源。

我们首先看看 FileInputStream 是怎么玩的:

public void close() throws IOException {

    synchronized (closeLock) {
        if (closed) {
            return;
        }
        closed = true;
    }

    if (channel != null) {
        fd.decrementAndGetUseCount();
        channel.close();
    }

    int useCount = fd.decrementAndGetUseCount();
    if ((useCount <= 0) || !isRunningFinalize()) {
        close0();
    }
}

要求此类的使用者不再使用此类的时候调用 close 终止方法,进行释放资源,并且在类中添加一个 closed 来标记资源是否已经释放,如果已经被释放了,那此类中的方法如果再被调用的话就抛出异常。

抽象形式如下:

class MyObject{
    private boolean isClosed = false;
    //终止方法
    public void close(){
        //资源释放操作...
        isClosed = true;
    }
}
public static void main(String... args) {
    MyObject object = new MyObject();
    try{
        //在这里使用调用方法...
    }  finally {
        //在这里关闭
        object.close();
    }
}

终结方法的用途

再继续往下看,就会看到 FileInputStream 还是有覆盖 finalize方法的。

protected void finalize() throws IOException {
    if ((fd != null) &&  (fd != FileDescriptor.in)) {
        runningFinalize.set(Boolean.TRUE);
         try {
             close();
         } finally {
             runningFinalize.set(Boolean.FALSE);
         }
    }
}

清楚的看到方法里面进行调用了 close 方法,这是为了当对象持有者忘记调用前面段落中建议的显式终结方法 :close()的情况下,使用inalize 方法充当“安全网”这是终结方法是用途之一。

抽象形式如下:

class MyObject{
    private boolean isClosed = false;
    //终止方法
    public void close(){
        //资源释放操作...
        isClosed = true;
    }

    //安全网
    protected void finalize() throws Throwable {
        try{
            close();
        }  finally  {
            super.finalize();
        }
    }
}

终结方法还有一个好处,把终结方法放在一个匿名的类,该匿名类的唯一用途就是终结它的外围实例。
外围实例持有对终结方法守卫者的唯一实例,这意味着当外围实例是不可达时,这个终结方法守卫者也是不可达的了,垃圾回收器回收外围实例的同时也会回收终结方法守卫者的实例,而终结方法守卫者的finalize方法就把外围实例的资源释放掉,就好像是终结方法是外围实例的一个方法一样。

看看 java.util.Timer 的终结方法守卫者:

public void cancel() {
    synchronized(queue) {
        thread.newTasksMayBeScheduled = false;
        queue.clear();
        queue.notify();  // In case queue was already empty.
    }
}

private final Object threadReaper = new Object() {
    protected void finalize() throws Throwable {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.notify(); // In case queue is empty.
        }
    }
};

cancel 方法是 Timer 提供的显式终止方法,threadReaper 是一个私有变量,保证除了实例本身外没有对它的引用,它覆盖 finalize 方法,实现与 cancel 方法一样的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值