我发现这个在线代码是在取消初始化Excel Interop对象后附加的:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
这是对DRY的准违反(以口吃的方式连续两次调用GC.Collect()和GC.WaitForPendingFinalizers())有什么用处,或者只是浪费时间?
答案 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两次将没有太大的区别......