具有挂起终结器的对象是否需要被 GC 多次收集?

Does an object with a pending finalizer need to be collected by the GC more than one time?

我正在阅读 C# 8.0 in a Nutshell 一书的“处理和垃圾收集”一章。当谈到终结者时,它说:

The GC identifies the unused objects for deletion, those without finalizers are deleted immediately, those with pending finalizers are kept alive and are put onto a special queue. When the garbage collection is complete and your program continues executing, the finalizer thread then starts running in parallel to the program, picking objects off that special queue and running their finalization methods.

这一段是不是说等待finalization的对象需要再次被GC回收?我以为它已经被GC检测为垃圾了,为什么还需要在完成后再次收集?

GC 的工作原理是从 GC 根开始遍历对象图。当 GC 执行收集时,它会检查没有引用它的对象(因此可以安全释放)。

终结器延迟了对象的垃圾回收。

为什么?好吧,GC 看到一个对象可以安全地被释放(没有连接到 GC 根)。但是,如果存在尚未 运行 的终结器,则无法释放内存。

因此 GC 将对象标记 为具有挂起的终结器并且 not 在第一次通过时释放 space . GC 运行 在那一刻也不是终结器(它将它放入“待定终结器”队列中)。

这就是为什么除非必要,否则使用终结器是不好的做法。它延迟收集。有些人误以为 GC 运行 是收集过程中的终结器。它没有。

什么时候需要?一个好的经验法则是,如果对象引用非托管内存(不由 GC 处理),那么您绝对应该使用终结器来避免内存泄漏。如果您仅引用托管对象,则不要引用。

如果你确实实现了终结器,我也会实现 IDisposable,释放 Dispose 上的任何非托管资源,并从 运行 GC.SuppressFinalize(this).

嗯,对象不是 'collected' 第一次。他们被认为需要额外的处理(终结器代码需要 运行)并放入终结队列,以便它们可以单独处理。这最终将它们放在 'freachable' 队列中,该队列现在已经复活了该对象:它现在被 freachable 队列引用并且不再符合收集条件。在终结器实际执行并且对象从 freachable 队列中移除后,它将无法访问。

(这是它过去的工作方式,不确定在较新的 .NET 版本中是否发生了变化,但我不知道有任何变化。)

所以这个对象并不是真的'collected'不止一次,如果通过'collected'我们就明白内存被回收了。但是,它确实需要额外的处理,稍后将由 GC 再次 re-evaluated。