如何正确实现终结器以检测Java中的资源泄漏

Sou*_*man 5 java resources garbage-collection finalizer

假设我已经使用close()方法创建了一些用于清理资源的资源类,并且如果有人忘记调用close(),我想覆盖finalize()以释放资源(并打印警告).怎么能正确完成?

  • 是否仅推荐用于本机(JNI分配)资源?
  • 如果您使用对终结器中已完成的另一个对象的引用会发生什么?如果存在循环依赖关系,我不会看到垃圾收集器如何阻止您访问可能已执行终结器的对象.
  • 是否有更好的替代方法来覆盖finalize()以检测和/或处理资源泄漏?
  • 实施终结器时需要注意的其他任何陷阱?

注意:我知道使用finalize()通常是个坏主意,并且不能保证被调用,还有其他一些问题在讨论这个问题.这个问题具体是关于如何在Java中实现终结器,而不是为什么你应该(或不应该).

Ami*_*far 5

在有效的java(第2版)中,约书亚在第7项中详细介绍了如何做到这一点.他首先暗示你几乎不应该使用finalizers.但是,使用它只打印一个日志语句说你有资源泄漏的一个原因.他说这样做的其中一个缺点就是有人可以扩展你的课程并且没有正确地调用超级终结者.所以他建议在子类中做这样的事情:

// Manual finalizer chaining
   @Override protected void finalize() throws Throwable {
       try {
           ... // Finalize subclass state
       } finally {
           super.finalize();
   } 
}
Run Code Online (Sandbox Code Playgroud)

这是为了确保如果在当前类中出现问题,finally仍然会调用它.这可能是一个糟糕的解决方案,因为它取决于对您的类进行子类化的人.另一种解决方案是使用监护对象文件来完成此操作.这看起来像:

// Finalizer Guardian idiom
   public class Foo {
// Sole purpose of this object is to finalize outer Foo object
      private final Object finalizerGuardian = new Object() {
         @Override protected void finalize() throws Throwable {
            ... // Finalize outer Foo object
         }
      };
      ...  // Remainder omitted
}
Run Code Online (Sandbox Code Playgroud)

这是一种更简洁的方法,因为您知道没有人可以覆盖该功能.

关闭资源的建议方法仍在实施Closeable,并确保由用户关闭.正如Josuha建议的那样,你不应该对finalize方法进行任何时间敏感的操作.JVM可能会选择在将来的某个时间运行它.如果你依赖这种方法做提交或重要的事情那么这是一个坏主意.