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

时间:2016-06-23 19:41:00

标签: c# garbage-collection office-interop collect

我发现这个在线代码是在取消初始化Excel Interop对象后附加的:

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

这是对DRY的准违反(以口吃的方式连续两次调用GC.Collect()和GC.WaitForPendingFinalizers())有什么用处,或者只是浪费时间?

3 个答案:

答案 0 :(得分:5)

  

垃圾收集器是否可以从多个呼叫中受益......

你不是这样做有利于GC,远非如此,你这样做是为了强制Excel.exe终止。它不能停止,直到所有的interop包装器(RCWs)完成。单个GC.Collect()调用足以设置该列车运动。

随后的GC.WaitForPendingFinalizers()调用是可选的。没有什么理由想要等到它们完成。在一个表现良好的程序中,这最多只能在几毫秒内完成。如果拥有这些interop包装器的线程将要终止,那么这一点非常重要。这并不常见。如果您不确定那么使用它并没有错。

第二次收集+等待呼叫没用。 RCW非常小,因此Collect调用不会释放任何有用的内存量。等等无法等待。

此代码的放置往往很重要。如果它出现在使用Excel接口的相同方法中,则在附加调试器时它将不会执行任何操作。将它放在方法的调用者中是最好的。调试器为什么起作用的解释在this post中解释。

答案 1 :(得分:1)

来自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();
  

当垃圾收集器找到可以回收的对象时,它会检查每个对象以确定对象的最终要求。如果一个对象实现了一个终结器并且没有通过调用SuppressFinalize来禁用终结,那么该对象将放在一个标记为准备完成的对象列表中。垃圾收集器为此列表中的对象调用Finalize方法,并从列表中删除条目。此方法将阻塞,直到所有终结器都运行完成。   

  运行终结器的线程未指定,因此无法保证此方法将终止。但是,当WaitForPendingFinalizers方法正在进行时,此线程可能被另一个线程中断。例如,您可以启动另一个等待一段时间的线程,然后在该线程仍然被挂起时中断该线程。

从我所知道的情况来看,并没有明确指出两次调用它的好处。

答案 2 :(得分:1)

简短的回答是,不,你不应该两次打电话。

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

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

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