为什么Java 9中finalize()
方法已被弃用?
是的,它可能会被错误地使用(例如仅一次保存对象免受垃圾收集或尝试在其中关闭一些本地资源,虽然这比不关闭好)。同样,许多其他方法也可能被错误地使用。
那么,finalize()
是否真的如此危险或绝对无用,以至于必须将其从Java中删除?
(这个问题不同于为什么要实现finalize()?那个问题是关于Java平台的弃用,而另一个问题则是关于在应用程序中是否应该使用这种机制。)
为什么Java 9中finalize()
方法已被弃用?
是的,它可能会被错误地使用(例如仅一次保存对象免受垃圾收集或尝试在其中关闭一些本地资源,虽然这比不关闭好)。同样,许多其他方法也可能被错误地使用。
那么,finalize()
是否真的如此危险或绝对无用,以至于必须将其从Java中删除?
(这个问题不同于为什么要实现finalize()?那个问题是关于Java平台的弃用,而另一个问题则是关于在应用程序中是否应该使用这种机制。)
Object.finalize
方法的,但实际上涉及到整个finalization机制。这个机制不仅包括表面APIObject.finalize
,还包括编程语言对对象生命周期的规定以及对JVM中垃圾回收器实现的实际影响。finalize
方法一次,即使该对象被“复活”以上是使用终结器的困难之处。考虑使用终结器的人应该重新考虑,鉴于上述问题列表。但是,这些问题是否足以废弃Java平台上的终结器?下面的部分解释了几个额外的原因。
终结可能使系统变得脆弱
即使您编写了正确使用终结的对象,当您的对象集成到更大的系统中时,它也可能会引起问题。即使您根本不使用终结,在集成到某些使用终结的更大系统的部分中,也可能会出现问题。一般的问题在于创建垃圾的工作线程需要与垃圾收集器保持平衡。如果垃圾收集器落后,至少有一些收集器可以“停止世界”并进行完整的收集以赶上。终结会使这种交互变得复杂。即使垃圾收集器跟上了应用程序线程,终结也可能引入瓶颈并减慢系统速度,或者它可能导致释放资源的延迟,从而耗尽这些资源。这是一个系统问题。即使实际使用终结的代码是正确的,在正确编程的系统中仍然可能出现问题。因此,平台中finalization机制的存在给试图编写高保证代码的程序员带来了负担。非final类的部分初始化实例可以通过finalizer攻击进行访问。攻击者在子类中覆盖受保护的
finalize
方法,并尝试创建该子类的新实例。此尝试失败......但攻击者只需忽略任何异常并等待虚拟机对部分初始化对象进行finalization。当发生这种情况时,恶意的finalize
方法实现被调用,使攻击者可以访问this
,即正在进行finalization的对象的引用。虽然对象只是部分初始化,但攻击者仍然可以调用它的方法......
Finalization增加了规范的复杂性
Java平台由多个规范定义,包括语言、虚拟机和类库API的规范。finalization的影响分散在所有这些规范中,但它反复表现出其存在。例如,finalization与对象创建有非常微妙的交互(已经足够复杂了)。finalization也已经出现在Java的公共API中,这意味着这些API的演变(直到现在)必须保持与先前指定的行为兼容。finalization的存在使得演化这些规范更加昂贵。Object.finalize
方法,但尚未标记它以供删除。这是一个正式的建议,程序员停止使用此机制。非正式地已知不应使用finalization,但当然需要采取正式措施。此外,某些库类中的finalize
方法(例如ZipFile.finalize
)已被弃用“待删除”,这意味着这些类的finalization行为可能会在将来的版本中被删除。最终,我们希望在JVM中禁用finalization(可能首先是可选的,然后是默认的),并在将来的某个时间点从垃圾回收器中实际删除finalization实现。(编辑于2022-04-04: JEP 421 弃用终结功能以便移除已经被整合并交付于JDK 18.)
finalization可以很容易地破坏子类/超类关系
如何实现? - Govinda Sakharefinalize()
方法修复这个问题。你只能允许重写或禁止重写。你不能有一个不可重写的操作并支持子类终结。 - Holger