为什么java和c#中有终结器?

RCI*_*CIX 11 c# java language-features finalizer

我不太明白为什么有java和c#等语言的终结器.AFAIK,他们:

  • 不保证运行(在java中)
  • 如果它们确实运行,它们可能会在有问题的对象成为最终确定的候选者之后运行任意时间量
  • 并且(至少在java中),即使坚持上课,它们也会产生惊人的巨大性能.

那他们为什么要加入呢?我问了一个朋友,他嘟something了一些关于"你想尽可能地清理像数据库连接这样的东西"的东西,但这让我感到很不好.你为什么要依赖具有上述属性的东西来做任何事情,即使是作为最后一道防线?特别是当如果在任何API中设计类似的东西时,所述API将会被笑掉.

Ree*_*sey 16

嗯,在某些情况下,它们非常有用.

在.NET CLR中,例如:

  • 不保证会运行

如果程序没有被杀死,终结器将始终最终运行.关于何时运行它并不确定.

  • 如果它们确实运行,它们可能会在有问题的对象成为最终确定的候选者之后运行任意时间量

确实如此,它们仍在运行.

在.NET中,这非常非常有用.在.NET中将原生的非.NET资源包装到.NET类中是很常见的.通过实现终结器,您可以保证正确清理本机资源.如果没有这个,用户将被迫调用一个方法来执行清理,这会大大降低垃圾收集器的效率.

通过实现终结器来确定何时释放您的(本机)资源并不总是很容易,即使您的类以不太完美的方式使用,您也可以保证它们能够正确清理.

  • 并且(至少在java中),即使坚持上课,它们也会产生惊人的巨大性能

同样,.NET CLR的GC在这里具有优势.如果您实现了正确的接口(IDisposable),那么如果开发人员正确实现了它,那么您可以防止发生最终的昂贵部分.这样做的方法是用户定义的清理方法可以调用GC.SuppressFinalize,它绕过终结器.

这为您提供了两全其美的优势 - 您可以实现终结器和IDisposable.如果您的用户正确处理您的对象,则终结器没有任何影响.如果他们不这样做,终结器(最终)将运行并清理您的非托管资源,但在运行时会遇到(小)性能损失.

  • 实际上,他们最终会运行.这更多的是java中的线程优先级问题,以及java GC的架构方式.不幸的是,在实际应用中,它们并不总是被执行.从技术上讲,它们最终应该运行,即使是在Java中. (2认同)
  • 死锁的终结器将阻止所有剩余的终结器在顺序运行时运行。 (2认同)

Han*_*ant 10

Hmya,你得到的照片画得有点过于乐观.终结器也不能保证在.NET中运行.典型的错误是终结器,它会在终结器线程上抛出异常或超时(2秒).

当Microsoft决定在SQL Server中提供.NET托管支持时,这是一个问题.重新启动应用程序以解决资源泄漏的应用程序类型不被视为可行的解决方法..NET 2.0获得了关键终结器,通过派生CriticalFinalizerObject类来实现.这样一个类的终结器必须遵守约束执行区域(CERs)的规则,本质上是一个禁止异常的代码区域.您在CER中可以做的事情非常有限.

回到最初的问题,终结器是释放内存以外的操作系统资源所必需的.垃圾收集器管理内存非常好,但没有做任何事情来释放笔,画笔,文件,套接字,窗口,管道等.当一个对象使用这样的资源时,它必须确保在完成后释放资源它.即使程序忘记了,终结器也能确保发生这种情况.您几乎从不用自己的终结器编写类,操作资源由框架中的类包装.

.NET框架还有一个编程模式,以确保尽早释放这样的资源,以便在终结器运行之前资源不会延续.所有具有终结器的类也实现了IDisposable.Dispose()方法,允许您的代码显式释放资源.这通常被.NET程序员所遗忘,但这通常不会导致问题,因为终结器确保最终完成.许多.NET程序员已经失去了几个小时的睡眠,担心是否所有的Dispose()调用都得到了解决,并且大量的线程已经在论坛上启动了.Java人一定要快乐得多.


跟进您的评论:终结器线程中的异常和超时是您不必担心的.首先,如果你发现自己正在写一个终结者,请深呼吸,问问自己是否在正确的道路上.终结器用于框架类,您应该使用这样的类来使用操作资源,您将免费获得内置于该类的终结器.一直到SafeHandle类,他们都有一个关键的终结器.

其次,终结器线程故障是严重的程序故障.类似于获取OutOfMemory异常或绊倒电源线并拔出机器.除了修复代码中的错误或重新布线之外,您无法对它们做任何事情.微软设计关键终结器非常重要,他们不能依赖为SQL Server编写.NET代码的所有程序员来正确获取代码.如果你自己摸索终结者,那么就没有这样的责任,你将接到客户的电话,而不是微软.