垃圾收集器是否受益于对 Collect 和 WaitForPendingFinalizers() 的多次调用?

Does the Garbage Collector benefit from multiple calls to Collect and WaitForPendingFinalizers()?

我在网上找到了这段代码,它是在取消初始化 Excel Interop 对象后附加的:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

这种对 DRY 的准违反(以结结巴巴的方式连续两次调用 GC.Collect() 和 GC.WaitForPendingFinalizers())是否有任何帮助,还是只是浪费时间?

这来自 Microsoft documentation 站点上的示例程序。

   //Force garbage collection.
   GC.Collect();

   // Wait for all finalizers to complete before continuing.
   // Without this call to GC.WaitForPendingFinalizers, 
   // the worker loop below might execute at the same time 
   // as the finalizers.
   // With this call, the worker loop executes only after
   // all finalizers have been called.
   GC.WaitForPendingFinalizers();

When the garbage collector finds objects that can be reclaimed, it checks each object to determine the object's finalization requirements. If an object implements a finalizer and has not disabled finalization by calling SuppressFinalize, the object is placed in a list of objects that are marked as ready for finalization. The garbage collector calls the Finalize methods for the objects in this list and removes the entries from the list. This method blocks until all finalizers have run to completion.

The thread on which finalizers are run is unspecified, so there is no guarantee that this method will terminate. However, this thread can be interrupted by another thread while the WaitForPendingFinalizers method is in progress. For example, you can start another thread that waits for a period of time and then interrupts this thread if this thread is still suspended.

据我所知,没有明确的迹象表明调用它两次有好处。

Does the Garbage Collector benefit from multiple calls...

您这样做不是为了让 GC 受益,远非如此,您这样做是为了强制 Excel.exe 终止。在所有互操作包装器 (RCW) 完成之前,它无法停止。一次 GC.Collect() 调用就足以启动该列车。

随后的 GC.WaitForPendingFinalizers() 调用是可选的。没有什么理由想等到他们完成。在 well-behaved 程序中,这最多发生在几毫秒内。仅当拥有这些互操作包装器的线程将要终止时,才需要执行此操作。这并不常见。如果您不确定,那么使用它并没有错。

第二个 Collect+Wait 调用没有用。 RCW 非常小,因此 Collect 调用不会释放任何有用的内存量。等待不能有任何等待。

此代码的放置往往很重要。如果它出现在使用 Excel 接口的相同方法中,那么在附加调试器时它不会做任何事情。最好将它放在方法的调用者中。 this post.

中解释了调试器发挥作用的原因

简而言之,不需要,您不需要调用它两次。

现在,让我们深入探讨一下。 GC 使用 generation-based 方法。也就是说 - 堆被分成块,用于优化 GC,因此它实际上 运行s 在较小的内存区域。您可以详细了解 GC 的工作原理 here.

当 GC 运行s 时,它会回收当前代的所有对象,这些对象没有强*引用(* - 可以使用 System.WeakReference 类型来引用一个对象,同时允许 GC 回收它)。那些在垃圾收集中幸存下来的对象被移入下一代。如果下一代足够满,GC 也会对其进行 运行 - 进一步移动幸存的对象。 话虽如此,您可以看到 运行第二次 GC 很可能没有什么区别。

但是,在负载很重的场景下,对象的创建速度非常快,两次后续调用之间的时间间隔就足够了(取决于线程的状态,优先级-哪个在进行调用时)以创建足够的对象,这可以增加内存的重量。在那种情况下,后续调用会产生影响并清理大量内存。虽然这种情况有可能发生,但它并不是很常见。原因是如果有这样的负载,它可能会继续,所以两次调用GC不会有太大的区别...