如何在Java中正确实现用于检测资源泄漏的终结器
假设我创建了一个资源类,它使用close()方法清理资源,如果有人忘记调用close(),我想重写finalize()释放资源(并打印警告)。如何正确地做到这一点如何在Java中正确实现用于检测资源泄漏的终结器,java,resources,garbage-collection,finalizer,Java,Resources,Garbage Collection,Finalizer,假设我创建了一个资源类,它使用close()方法清理资源,如果有人忘记调用close(),我想重写finalize()释放资源(并打印警告)。如何正确地做到这一点 是否只建议本机(JNI分配的)资源使用 如果使用来自终结器的对已终结的另一个对象的引用,会发生什么情况?如果存在循环依赖关系,我看不出垃圾收集器如何阻止您访问可能已执行终结器的对象 在检测和/或处理资源泄漏时,除了覆盖finalize()之外,还有其他更好的方法吗 在实现终结器时还需要注意哪些陷阱 注意:我知道使用finalize
- 是否只建议本机(JNI分配的)资源使用
- 如果使用来自终结器的对已终结的另一个对象的引用,会发生什么情况?如果存在循环依赖关系,我看不出垃圾收集器如何阻止您访问可能已执行终结器的对象
- 在检测和/或处理资源泄漏时,除了覆盖finalize()之外,还有其他更好的方法吗
- 在实现终结器时还需要注意哪些陷阱
finalizer
s。但是,使用它只打印一条日志语句的一个原因是,您有一个资源泄漏。他说,这样做的一个缺点是有人可以扩展你的类,而不能正确地调用超级终结器。因此,他建议在子类中执行类似的操作:
// Manual finalizer chaining
@Override protected void finalize() throws Throwable {
try {
... // Finalize subclass state
} finally {
super.finalize();
}
}
这是为了确保如果在当前类中出现故障,finally
仍将被调用。这可能是一个糟糕的解决方案,因为它取决于对类进行子类化的人。另一种解决方案是使用guardian对象文件来完成此操作。这看起来像:
// 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
}
这是一种更干净的方法,因为您知道没有人可以覆盖该功能
建议关闭资源的方法仍然是实现Closeable
,并确保由用户关闭。正如Josuha所建议的,您不应该在finalize
方法中执行任何时间敏感的操作。JVM可能会选择在将来某个时候运行它。如果您依赖此方法来执行提交或其他重要操作,那么这是一个坏主意
是否只建议本机(JNI分配的)资源使用
不可以。您的用例对于终结器也是有效的。我的意思是记录资源泄漏
如果访问另一个已被访问的Java对象,会发生什么
在终结器中终结
如果您仍然可以访问它,则它尚未最终确定。或者我在你的问题中遗漏了什么 我现在明白了。可能是A和B都有资格进行垃圾收集,并且它们之间存在引用。这应该没有问题,因为默认情况下,
finalize()
什么都不做。如果您为两个对象都编写自定义finalize()
方法,那么您应该独立于它们的定稿顺序来编写代码。您还应该防止引用变成null
,因为相应的对象可能已经被垃圾收集
是否有更好的替代方法来替代
检测和/或处理资源泄漏
我认为使用终结器时最重要的事情是检测和记录/警告泄漏,而不是处理泄漏。记录此泄漏的最佳时机是在垃圾收集资源对象之前。因此,终结器自然适合于此
我想强调一点:我不会使用终结器来处理忘记关闭资源的程序员,而是告诉他需要修复代码
在实现终结器时还需要注意哪些陷阱
对于具有终结器的对象,似乎也存在性能损失。你应该做一个测试看看你的表现如何。如果有一个重要的性能损失,我会尝试想象一种仅在开发时使用终结器记录资源泄漏的机制
并且按照Amir Raminfar的建议使用终结器继承对象时要小心
还有一件事:看看[
FileInputStream][1]
或[FileOutputStream][2]
的源代码,它们使用终结器的原因相同。我希望我的答案能有所帮助。如果我遗漏了什么,请告诉我。重新表述了关于从终结器访问可能已完成的对象的第二点。仍然很好奇这意味着什么。“如果你仍然可以访问它,它还没有最终确定。或者我在你的问题中遗漏了什么。”但是如果对象A引用了对象B,并且B在A之前最终确定,那么A不能从它的终结器访问B吗?编辑了我的帖子并重新表述了这个问题。所以当调用终结器时,引用垃圾收集对象的字段将null
?偶数final
字段?是的,偶数字段<代码>最终版指定引用一旦初始化就不会更改。