在什么情况下,我们需要两次调用GC.Collect

时间:2010-09-30 11:01:57

标签: .net garbage-collection

我们有一个WPF应用程序,基于Unity和MMVVVM模式。在应用程序生命周期中,可能存在多个项目生命周期,在每个项目生命周期之后,我们执行手动拆除并尝试释放ViewModel的所有引用。对于使用Unity的事件订阅,我们使用弱引用。因此我们假设在拆除之后,我们可以调用GC Collect,以便所有垃圾对象都被垃圾收集。我们还有另一个手动取消订阅所有事件的选项,但我们更喜欢垃圾收集,因为它将为我们清除大约200MB,这将有助于新项目加载。

有一个实例,我们观察到,如果我只调用GC.Collect一次,它的引用仍然会在内存中保留一段时间。

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

但如果我尝试连续两次调用GC,它会很好地清理所有内容。

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

任何想法或指示都将受到高度赞赏。

更新:

类中没有定义终结器。

现在我也在考虑一个案例,其中这个对象在另一个可能有终结器的对象中被引用。在我们的框架中,我们只有DBProvider的终结器,所以我不认为,即使是这种情况。

4 个答案:

答案 0 :(得分:11)

听起来你有终结器的东西,基本上 - 如果你只调用GC.Collect()一次,终结器正在完成但是最终的对象没有被收集。

这是否代表一个错误是另一回事。通常情况下,确实需要执行终结器并不是一个好主意,但也许在你的情况下也没关系。

答案 1 :(得分:6)

  

如果我只调用GC.Collect一次,它的引用仍会在内存中保留一段时间。

不是很奇怪。当一个对象有一个Finalizer(并且没有调用GC.SuppressFinalize())时,它会停止执行(它收集,因此终结器可以使用有效对象运行) 。此对象引用的所有实例也会停止执行。需要通过GC进行第二轮清理。

另一方面,包含大型和复杂程序的大多数程序应该能够在不调用GC.Collect()的情况下运行。你想打电话两次......

  

在每个项目生命周期后,我们都会进行手动拆除

听起来很复杂且容易避免...... 进入您的域/视图模型有多少个引用?理想情况下,您只需剪切1或2个对“主”对象的引用而忘记它。

答案 2 :(得分:0)

@Nitin,我不确定我的建议是否会对您有所帮助,但根据经验,我们应该避免显式调用GC.Collect()。因为它可能会导致性能问题。而是尝试遵循适当的处置模式。

答案 3 :(得分:0)

您有一个弱引用,您希望将其设置为null,因为您调用GC并且您认为没有任何东西可以使对象保持活动状态。

我不知道.net中如何实现弱引用,但它们很可能会延迟对象的收集并使用像终结器这样的系统。

或者你可能有一个需要最终确定的对象,因为你没有调用dispose()。 (在WPF或WinForms中很常见)

要了解一下,您需要使用MemoryProfiler,期望花费至少一天时间来学习如何使用任何内存分析器,因为您将看到有关WPF内部对象的大量数据。就个人而言,我会下载Redgate’s memory profiler的免费路径,并且看看问题是什么,支持也很有帮助。 (其他内存分析器也很好用,在很大程度上它只取决于你习惯的东西。)