当我们不知道何时可能运行任何给定对象的 finalize() 方法时,我们需要在 java 中重写 finalize() 方法的地方?我们可以在 finalize() 中关闭哪些类型的资源?GC 时的最佳机会是什么?会调用 finalize() 方法吗?
4 回答
首先,终结的目的是让无法访问的对象有机会在对象被垃圾收集之前执行任何清理。
例如,关闭打开的数据库连接。
应该为对象重写该finailze()
方法以包含清理代码或处理在对象被垃圾收集之前应该完成的系统资源。
关于GC 调用 finalize() 方法的最佳机会是什么?,我们可以通过两种方式请求JVM执行垃圾回收:
Runtime.getRuntime().gc();
System.gc();
我们可以在 finalize() 中关闭什么类型的资源,答案有两种情况:
null
在创建对象的目的完成后设置所有可用的对象引用。使引用变量引用另一个对象:将引用变量与对象解耦并将其设置为引用另一个对象,因此在重新分配之前它所引用的对象符合Grabage Collection的条件。
finalize
当您的类具有不会被 GC 清理的资源(例如文件句柄或数据库连接)时,您应该覆盖。这些资源应该在应用程序代码中清理,因为正如你所说,我们不知道什么时候finalize
会运行,但是最好在终结器中清理这些资源,以防程序员搞砸并离开资源打开(如果资源在运行时仍然打开,finalize
则将其记录为警告或错误,因为这意味着应用程序代码没有正确清理资源)。
如果程序员通过调用正确地清理了对象的资源,.NET 可以让您抑制终结器dispose
,但不幸的是,我不认为 Java 允许类似的模式。
从我的笔记中:
在对象被垃圾回收之前调用名为finalize()的方法。不需要释放任何对象,但可能需要释放一些其他资源(例如打开的文件描述符)。
protected void finalize() {
if( fd != null ) {
try {
closeIt() ;
} catch (IOException e) {
// print an error something, or otherwise try to recover
}
}
}
从理论上讲,终结器可能会做一些事情来防止对象被垃圾收集(例如,将“this”存储在某个引用中)。这从来都不是一件有用的事情。
与构造函数不同,终结器不会自动链接。手动链接它们是一个非常好的主意,最好是在终结器的末尾:
protected void finalize() {
// free resources consumed by this class
// chain upward:
super.finalize() ;
}
在大多数情况下,您几乎不需要编写 finalizer()。
编辑:此答案的早期版本将 finalize() 声明为抛出 IOException。回想起来,这可能是一个非常糟糕的主意。我不知道我在想什么。
最好在您手动调用的关闭方法中关闭资源。finalize 仅在对象被垃圾收集时才被调用,这可能要等到您使用完对象很久之后才会调用。
我曾经覆盖 finalize 的唯一原因是在我的应用程序中调试内存使用情况,其中一些对象没有被收集。