简体   繁体   English

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

[英]How to properly implement a finalizer for detecting resource leaks in Java

Let's say I have created some resource class with a close() method for cleaning up the resource, and I want to override finalize() to free the resource (and print a warning) if someone has forgotten to call close(). 假设我已经使用close()方法创建了一些用于清理资源的资源类,并且如果有人忘记调用close(),我想覆盖finalize()以释放资源(并打印警告)。 How can this be done properly? 怎么能正确完成?

  • Is it recommended only for native (JNI-allocated) resources? 是否仅推荐用于本机(JNI分配)资源?
  • What happens if you use a reference to another object that has been finalized, from a finalizer? 如果您使用对终结器中已完成的另一个对象的引用会发生什么? If there are cyclic dependencies I don't see how the garbage collector can prevent you from accessing objects whose finalizers could have been executed. 如果存在循环依赖关系,我不会看到垃圾收集器如何阻止您访问可能已执行终结器的对象。
  • Are there any better alternatives to overriding finalize() for detecting and/or dealing with resource leaks? 是否有更好的替代方法来覆盖finalize()以检测和/或处理资源泄漏?
  • Any other pitfalls to be aware of when implementing a finalizer? 在实施终结器时需要注意的其他任何陷阱?

Note: I know that using finalize() is usually bad idea, and that it's not guaranteed to be called, there are several other questions discussing this. 注意:我知道使用finalize()通常是个坏主意,并且不能保证被调用,还有其他一些问题在讨论这个问题。 This question is specifically about how to implement a finalizer in Java, not about why you should (or shouldn't). 这个问题具体是关于如何在Java中实现终结器,而不是为什么你应该(或不应该)。

In effective java (2nd edition) , Joshua goes in detail in Item #7 about how you can do this. 有效的java(第2版)中 ,约书亚在第7项中详细介绍了如何做到这一点。 He first suggests that you should almost never use finalizer s. 他首先建议你几乎不要使用finalizer However, one reason to use it to only print a log statement saying you have a resource leak. 但是,使用它只打印一个日志语句说你有资源泄漏的一个原因。 He says one of the draw backs to doing it this way is that someone can extend your class and not properly call the super finalizer. 他说这样做的其中一个缺点就是有人可以扩展你的课程并且没有正确地调用超级终结者。 So he suggests to do something like this in the subclass: 所以他建议在子类中做这样的事情:

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

This is to ensure that if something breaks in the current class the finally will still get called. 这是为了确保如果当前类中的某些内容中断,则仍然会调用finally This maybe a bad solution because it depends on the person who is subclassing your class. 这可能是一个糟糕的解决方案,因为它取决于对您的类进行子类化的人。 An alternate solution is to use a guardian object file to get this done. 另一种解决方案是使用监护对象文件来完成此操作。 This looks like: 这看起来像:

// 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
}

This is a cleaner approach because you know that no one can override that functionality. 这是一种更简洁的方法,因为您知道没有人可以覆盖该功能。

The suggested way to closing resources is still implementing Closeable and make sure it is up to the user to close. 关闭资源的建议方法仍然是实现Closeable ,并确保由用户关闭。 As Josuha suggests, you shouldn't do any time sensitive operations in the finalize method. 正如Josuha建议的那样,你不应该在finalize方法中做任何时间敏感的操作。 The JVM may choose to run it as sometime in the future. JVM可能会选择在将来的某个时间运行它。 If you are depending on this method to do commits or something important then that is a bad idea. 如果你依赖这种方法做提交或重要的事情那么这是一个坏主意。

Is it recommended only for native (JNI-allocated) resources? 是否仅推荐用于本机(JNI分配)资源?

No. Your use case is also a valid one for finalizers. 不。您的用例对于终结器也是有效的。 I mean logging resource leakage. 我的意思是记录资源泄漏。

What happens if you access another Java object that has been finalized, in the finalizer? 如果在终结器中访问已完成的另一个Java对象会发生什么?

If you can still access it, it is not finalized. 如果您仍然可以访问它,则尚未最终确定。 Or maybe I'm missing something in your question. 或许我在你的问题中遗漏了一些东西。

I understand now. 我现在知道了。 It might be the case that both A and B are eligible for garbage collection and there is a reference between them. 情况可能是A和B都有资格进行垃圾收集,并且它们之间存在参考。 It should be no problem as by default finalize() does nothing. 它应该没有问题,因为默认情况下, finalize()不执行任何操作。 If you write for both your objects custom finalize() methods you should write your code independent of the order they are finalized. 如果为自定义的finalize()方法编写对象,则应编写代码,而不依赖于最终确定的顺序。 You should also guard for the reference becoming null as the corresponding object might have been already garbage collected. 您还应该保护引用变为null因为相应的对象可能已经被垃圾收集。

Are there any better alternatives to overriding finalize() for detecting and/or dealing with resource leaks? 是否有更好的替代方法来覆盖finalize()以检测和/或处理资源泄漏?

I think the most important thing when using finalizers is detecting and logging/warning about the leakage not dealing with it. 我认为使用终结器最重要的是检测和记录/警告泄漏而不处理它。 And the best moment to log this leakage is before the resource object is garbage collected. 记录此泄漏的最佳时刻是在资源对象被垃圾回收之前。 So finalizers are a natural fit for this. 因此终结器非常适合这种情况。

I want to stress this: I wouldn't use finalizers to deal with a programmer that forgot to close the resource, but to inform him that he need to fix his code. 我想强调一点:我不会使用终结器来处理忘记关闭资源的程序员,而是告诉他需要修复他的代码。

Any other pitfalls to be aware of when implementing a finalizer? 在实施终结器时需要注意的其他任何陷阱?

It seems there is also a performance penalty with objects that have finalizers. 对于具有终结器的对象,似乎也存在性能损失。 You should make a test see how it goes for you. 你应该做一个测试,看看它是怎么回事。 If there is an important performance penalty I would try to imagine a mechanism to use finalizers only at development time to log resource leakage. 如果存在重要的性能损失,我会尝试设想仅在开发时使用终结器来记录资源泄漏的机制。

And take care when inheriting an object with finalizers as suggested by Amir Raminfar. 按照Amir Raminfar的建议,在使用终结器继承对象时要小心。

One more thing: take a look at the source code of [ FileInputStream][1] or [FileOutputStream][2] they use finalizers for the same reason. 还有一件事:看看[ FileInputStream][1][FileOutputStream][2]的源代码,他们使用终结器也是出于同样的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM