首页 > 学院 > 开发设计 > 正文

07-避免使用终结方法

2019-11-08 18:49:13
字体:
来源:转载
供稿:网友

本条目大意

尽量避免在类中使用终结(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 方法一样的功能。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表