C# 垃圾收集器是否从多次调用Collect和WaitForPendingFinalizers()中获益?

C# 垃圾收集器是否从多次调用Collect和WaitForPendingFinalizers()中获益?,c#,garbage-collection,office-interop,collect,C#,Garbage Collection,Office Interop,Collect,我在网上找到了在取消初始化Excel互操作对象后追加的以下代码: GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); 这种类似于DRY的违反(连续两次以口吃的方式调用GC.Collect()和GC.WaitForPendingFinalizers())是否有任何帮助,或者只是浪费时间?这来自Microsoft站点上的示例程序 当垃圾收集器找到可以回收的对象时,它会检查

我在网上找到了在取消初始化Excel互操作对象后追加的以下代码:

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

这种类似于DRY的违反(连续两次以口吃的方式调用GC.Collect()和GC.WaitForPendingFinalizers())是否有任何帮助,或者只是浪费时间?

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

当垃圾收集器找到可以回收的对象时,它会检查每个对象以确定对象的最终确定要求。如果一个对象实现了终结器,并且没有通过调用SuppressFinalize来禁用终结,那么该对象将被放置在标记为准备终结的对象列表中。垃圾收集器为此列表中的对象调用Finalize方法,并从列表中删除条目。此方法将一直阻止,直到所有终结器都运行完成。 运行终结器的线程未指定,因此无法保证此方法将终止。但是,当WaitForPendingFinalizers方法正在进行时,该线程可能会被另一个线程中断。例如,您可以启动另一个等待一段时间的线程,然后在该线程仍然挂起时中断该线程

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

垃圾收集器是否从多个调用中获益

您这样做并不是为了使GC受益,而是为了迫使Excel.exe终止。在完成所有互操作包装(RCW)之前,它无法停止。一个GC.Collect()调用就足以使列车运行

后续的GC.WaitForPendingFinalizers()调用是可选的。没有什么理由想等到它们完成。在一个行为良好的程序中,这种情况最多发生在几毫秒之内。只有当拥有这些互操作包装器的线程即将终止时,这样做才非常重要。这并不常见。如果您不确定,那么使用它并没有错

第二个Collect+Wait调用没有用处。RCW非常小,因此对方付费呼叫不会释放任何有用的内存量。等等,我不能再等了


这段代码的位置往往很重要。如果它出现在使用Excel接口的同一方法中,则在附加调试器时它不会执行任何操作。将它放在方法的调用者中是最好的。中解释了调试器发挥作用的原因。

简短的回答是“否”,您不需要调用它两次

现在,让我们再深入一点。GC使用基于生成的方法。也就是说,堆被分为多个块,这些块用于优化GC,因此它实际上在更小的内存区域上运行。您可以进一步了解GC的工作原理

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


但是在重载场景中,对象创建速度非常快,两次后续调用之间的时间间隔足以创建足够的对象(取决于线程的状态、优先级-调用的优先级),从而为内存增加一定的权重。在这种情况下,随后的调用可能会产生影响并清除大量内存。虽然这种情况有可能发生,但这种情况并不常见。原因是,如果存在这样的负载,它可能会继续,因此两次调用GC不会有太大区别…

请参阅代码注释如果对象具有终结器,则必须至少进行两次收集,然后才能完全回收内存。第一个集合只是将其放入终结队列。第二个将看到终结队列不再有引用,终结器已经运行,因此可以回收。@mikez:既然您写了,“…至少两个…”第三次迭代有什么好处吗?处理器追逐尾巴的上限是多少?在使用这种代码时,必须非常关键。请参阅和中的讨论。不,这里不需要第三个,因为
GC.Collect()
收集所有代。我指的是更广泛的垃圾收集,它可以被限制到特定的世代。从技术上讲,该对象通过放入终结队列而在收集过程中幸存下来。现在是第1代或第2代。即使终结器已运行,gen 0集合也不会看到它被回收。因此,任何一代至少有两件收藏品。我已经说过我会把奖金奖励给马特,我确实这么做了,但我会看看是否能在这里再奖励一件。如果可以的话,我会。@B.ClayShannon有可能给社区维基奖励吗?@Evk不知道你在问什么。有可能奖励奖金吗?是的,我已经做了数百万次了。我曾经得过13000分。@B.ClayShannon不,我是专门针对“社区维基”这样的答案。但我已经发现这是可能的。@Evk我明白了;我不知道一个人可以成为“社区维基”;听起来像夏威夷的节日。
   //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();