Android 编码规范:(七)避免使用终结方法

任何事情都存在其一定的双面性或者多面性,对于C++的开发者,内存资源是需要手工分配和释放的,而对于Java和C#这种资源托管的开发语言,更多的工作可以交给虚拟机的垃圾回收器来完成,由此C++程序得到了运行效率,却失去了安全。在Java的实际开发中,并非所有的资源都是可以被垃圾回收器自动释放的,如FileInputStream、Graphic2D等class中使用的底层操作系统资源句柄,并不会随着对象实例被GC回收而被释放,然而这些资源对于整个操作系统而言,都是非常重要的稀缺资源,更多的资源句柄泄露将会导致整个操作系统及其运行的各种服务程序的运行效率直线下降。那么如何保证系统资源不会被泄露了?在C++中,由于其资源完全交由开发者自行管理,因此在决定资源何时释放的问题上有着很优雅的支持,C++中的析构函数可以说是完成这一工作的天然候选者。任何在栈上声明的C++对象,当栈退出或者当前对象离开其作用域时,该对象实例的析构函数都会被自动调用,因此当函数中有任何异常(Exception)发生时,在栈被销毁之前,所有栈对象的析构函数均会被自动调用。然而对于Java的开发者而言,从语言自身视角来看,Java本身并未提供析构函数这样的机制,当然这也是和其资源被JVM托管有一定关系的。

在Java中完成这样的工作主要是依靠try-finally机制来协助完成的。然而Java中还提供了另外一种被称为finalizer的机制,使用者仅仅需要重载Object对象提供的finalize方法,这样当JVM的在进行垃圾回收时,就可以自动调用该方法。但是由于对象何时被垃圾收集的不确定性,以及finalizer给GC带来的性能上的影响,因此并不推荐使用者依靠该方法来达到关键资源释放的目的。比如,有数千个图形句柄都在等待被终结和回收,可惜的是执行终结方法的线程优先级要低于普通的工作者线程,这样就会有大量的图形句柄资源停留在finalizer的队列中而不能被及时的释放,最终导致了系统运行效率的下降,甚至还会引发JVM报出OutOfMemoryError的错误。

Java的语言规范中并没有保证该方法会被及时的执行,甚至都没有保证一定会被执行。即便开发者在code中手工调用了System.gc和System.runFinalization这两个方法,这仅仅是提高了finalizer被执行的几率而已。还有一点需要注意的是,被重载的finalize()方法中如果抛出异常,其栈帧轨迹是不会被打印出来的。在Java中被推荐的资源释放方法为,提供显式的具有良好命名的接口方法,如FileInputStream.close()和Graphic2D.dispose()等。然后使用者在finally区块中调用该方法,见如下代码:

  1. public void test() {    
  2.          FileInputStream fin = null;    
  3.          try {    
  4.              fin = new FileInputStream(filename);    
  5.              //do something.    
  6.          } finally {    
  7.              fin.close();    
  8.          }    
  9.      }   

那么在实际的开发中,利用finalizer又能给我们带来什么样的帮助呢?见下例:

  1. public class FinalizeTest {    
  2.          //@Override    
  3.          protected void finalize() throws Throwable {    
  4.              try {    
  5.                  //在调试过程中通过该方法,打印对象在被收集前的各种状态,    
  6.  //如判断是否仍有资源未被释放,或者是否有状态不一致的现象存在。    
  7.  //推荐将该finalize方法设计成仅在debug状态下可用,而在release    
  8.  //下该方法并不存在,以避免其对运行时效率的影响。    
  9.                  System.out.println("The current status: " + _myStatus);    
  10.              } finally {    
  11.                  //在finally中对超类finalize方法的调用是必须的,这样可以保证整个class继承    
  12.  //体系中的finalize链都被执行。    
  13.                  super.finalize();     
  14.              }    
  15.          }    
  16.      }   


posted @ 2012-06-04 20:23  移动应用开发  阅读(166)  评论(0编辑  收藏  举报