c# - 垃圾收集和终结器 : Finer Points

标签 c# garbage-collection finalizer

在回答关于 SO 的另一个问题*以及随后的评论讨论时,我在一个我不清楚的地方碰壁了。

在我误入歧途的任何地方纠正我......

当垃圾收集器收集一个对象时,它会在一个单独的线程上调用该对象的终结器(除非终结器已被抑制,例如通过 Dispose() 方法)。在收集时,GC 会挂起除触发收集的线程之外的所有线程(后台收集除外)。

不清楚的地方:

  1. 垃圾收集器是否在收集之前等待终结器在该对象上执行?
  2. 如果不是,它会在终结器仍在执行时取消挂起线程吗?
  3. 如果它等待,如果终结器遇到一个被挂起线程持有的锁,会发生什么情况?终结器线程是否死锁? (在我的回答中,我认为这是糟糕的设计,但我可能会看到可能发生这种情况的情况)

* 原始问题的链接:
.NET GC Accessing a synchronised object from a finalizer

最佳答案

Does the Garbage Collector wait for the finalizer to execute on that object before collecting it?

你的问题有点模棱两可。

当 GC 遇到需要终结的“死”对象时,它会放弃回收死对象存储的尝试。相反,它将对象放在“我知道需要终结的对象”队列中,将该对象视为事件对象,直到终结器线程完成处理它为止。

所以,是的,GC 会“等待”直到执行终结器,然后再回收存储。但它不会同步等待。听起来您在问“GC 是否会在那里同步调用终结器?”不,它会将对象排队等待稍后完成并继续进行。 GC希望快速完成释放垃圾和压缩内存的任务,以便程序本身尽快恢复运行。它不会停下来处理一些在清理之前需要引起注意的发牢骚的对象。它将该对象放在队列中并说“保持安静,终结器线程稍后会处理你”。

稍后 GC 将再次检查该对象并说“你还死了吗?你的终结器运行了吗?”如果答案为"is",则该对象将被回收。 (请记住,终结器可能会使死对象恢复为事件对象;尽量不要这样做。结果不会有任何令人愉快的事情发生。)

Does it un-suspend threads while the finalizer is still executing?

我相信 GC 解冻了它卡住的线程,并向终结器线程发出信号“嘿,你有工作要做”。因此,当终结器线程开始运行时,被 GC 卡住的线程将再次启动。

可能必须有未卡住的线程,因为终结器可能需要将调用编码到用户线程以释放线程相关资源。当然,其中一些用户线程可能被阻塞或卡住;线程总是会被某些东西阻塞。

what happens if the finalizer runs into a lock being held by one of the suspended threads? Does the finalizer thread deadlock?

没错。终结器线程没有什么神奇之处可以防止它死锁。如果用户线程正在等待终结器线程取出的锁,而终结器线程正在等待用户线程取出的锁,那么就会发生死锁。

终结器线程死锁的例子比比皆是。这是一篇关于此类场景的好文章,其中包含一系列指向其他场景的链接:

http://blogs.microsoft.co.il/blogs/sasha/archive/2010/06/30/sta-objects-and-the-finalizer-thread-tale-of-a-deadlock.aspx

正如文章所述:终结器是一种极其复杂且危险的清理机制,您应该尽可能避免使用它们。终结器错误非常容易,而正确则非常困难。

关于c# - 垃圾收集和终结器 : Finer Points,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5223325/

相关文章:

c# - 使用 System.Json 遍历 JSON

c# - Log4Net,如果重复则增加严重性

java - "java.lang.OutOfMemoryError: GC overhead limit exceeded"中 GC 时间过长的持续时间

python - 如何防止模块被垃圾收集器删除或在 __del__ 方法内重新导入

android - 为什么使用 Jacksons ObjectMapper Android 垃圾收集次数如此之多?

.net - 为什么从不调用 NET 垃圾收集器?

c# - 使用多个表中的数据创建下拉列表

c# - 跨层枚举

c# - 从终结器访问不可终结的对象

c# 析构函数(和终结器?)在程序结束时自动调用 - 我应该对此做些什么吗?