Java中最终化的目的是什么?

我对最终定稿的理解是这样的:

为了清理或回收对象占用的内存,垃圾收集器开始运行。 (自动被调用?)

垃圾收集器然后取消引用该对象。 有时,垃圾收集器无法访问该对象。 然后调用finalize进行最后的清理处理,之后可以调用垃圾收集器。

这是对最终确定的准确描述吗?

垃圾收集器在后台自动工作(尽管可以显式调用它,但对此的需求应该很少)。 它基本上只清理其他对象没有引用的对象(授予,完整的图片更复杂,但这是基本的想法)。 因此它不会更改任何活动对象中的任何引用。 如果无法从任何活动对象访问对象,则表示可以安全地对其进行垃圾回收。

完成意味着清理对象获取的资源(不是内存,而是其他资源,例如文件句柄,端口,数据库连接等)。 但是,它没有真正解决:-(

  • 调用finalize()时无法预测
  • 事实上,无法保证finalize()将永远被调用!

因此,即使它被保证被调用,它也不是释放资源的好地方:当它被调用以释放你打开的所有数据库连接时,系统可能已经完全耗尽了空闲连接,并且你的应用程序不再起作用了。

从这篇文章 :

实现finalize()方法的类的任何实例通常称为可终结对象。 当Java垃圾收集器不再被引用时,它们不会立即被回收。 相反,Java垃圾收集器将对象附加到特定队列以进行最终化过程。 通常它是由某些Java虚拟机上称为“引用处理程序”的特殊线程执行的。 在此最终化过程中,“Finalizer”线程将执行对象的每个finalize()方法。 只有在成功完成finalize()方法之后,才会将一个对象移交给Java垃圾收集,以便通过“future”垃圾收集来回收它的空间。

您可以在类的finalize()方法中自由地执行任何操作。 当您这样做时,请不要指望当不再引用或不再需要该对象时,Java垃圾收集器将回收每个对象占用的内存空间。 为什么? 无法保证finalize()方法将及时完成执行。 最坏的情况是,即使没有对该对象的更多引用,也可能无法调用它。 这意味着不能保证任何具有finalize()方法的对象都是垃圾回收的。

此外,Sun的这篇文章有一些很好的图表来解释这个过程。

不。 仅当垃圾收集器尝试回收您的对象时,才会运行finalize()方法。

您的对象使用的任何内存(通常,我都不会想到exception)会自动连接到您的对象并随之清理。 因此,终结并不意味着释放内存 ,而是用于与对象关联的任何其他资源。 例如,这可以用于关闭打开的文件或数据库连接,或者可能运行一些与操作系统连接的低级代码以释放一些系统级资源。

实际上,这是finalize()方法的行为:

一旦垃圾收集器运行(VM决定它需要释放内存,你不能强制它运行)并决定从这个对象收集内存(这意味着没有任何引用指向它,至少从可到达的对象) ,在它删除它占用的内存之前,它在对象上运行方法finalize()。 你可以肯定,如果收集垃圾,对象将在它消失之前运行finalize(),但你不能确定它会完全得到GC,所以你不应该依赖这个方法来进行任何消毒。 。 您应该在finally {}块中运行清理语句,而不是使用finalize(),因为它不能保证运行。

此外,有些人已经完成了性能测试并且表明finalize方法在某种程度上减慢了对象的创建/破坏。 我不记得来源所以对待这个信息不太可靠。 🙂

Finalization用于清理资源,垃圾收集器无法释放这些资源。 例如,考虑一个直接从OS分配(通过一些native API)资源的程序。 这通常会产生某种“句柄”(UNIX文件描述符或Windows HANDLE,或类似的东西):

 class Wrapper { private long handle; private Handle(long h) { handle = h; } private static native long getHandleFromOS(); static Wrapper allocate() { return new Handle(getHandleFromOS()); } } 

那么,如果您的代码分配了类Wrapper的实例,会发生什么? 那么类会分配某种特定于操作系统的资源,并在成员变量中保留对它的引用(句柄)。 但是,当对包装器实例的最后一次Java引用丢失时会发生什么? 现在,垃圾收集器将(在某些时候)回收现已解散的包装器实例的空间。 但是包装器分配的OS资源会发生什么? 如果它是一个昂贵的资源,例如文件描述符,它将在上述场景中泄露,这是一件坏事。

为了在这种情况下允许代码清理,有finalize方法。

 class Wrapper { private long handle; private Handle(long h) { handle = h; } protected void finalize() { returnHandleToOS(handle); } private static native long getHandleFromOS(); private static native void returnHandleToOS(long handle); static Wrapper allocate() { return new Handle(getHandleFromOS()); } } 

现在,当GC回收包装器实例的空间时,终结器确保资源正确地返回到OS。

这听起来很不错,但正如其他人已经指出的那样,缺点是,终结本质上是不可靠的:你不知道什么时候终结器会被运行。 更糟糕的是:根本无法保证它会被运行。 因此,最好提供一个dispose机制,并仅使用最终确定作为安全网,以防您的class级客户忘记妥善处理他们的参考:

 class Wrapper { private long handle; private Handle(long h) { handle = h; } protected void finalize() { if( handle != 0 ) returnHandleToOS(handle); } public void dispose() { returnHandleToOS(handle); handle = 0; } private static native long getHandleFromOS(); private static native void returnHandleToOS(long handle); static Wrapper allocate() { return new Handle(getHandleFromOS()); } }