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

11
我们有一个基于Unity和MVVM设计模式的WPF应用程序。在应用程序的生命周期内,可能会有多个项目生命周期,在每个项目生命周期之后,我们都会手动进行Tear Down并试图释放所有ViewModels的引用。对于Unity中的事件订阅,我们使用弱引用(weak references)。因此,我们假设在Tear Down之后,可以调用GC Collect,以便清除所有垃圾对象。我们还有另一种选项,即手动取消订阅所有事件,但是我们更喜欢垃圾回收,因为它将为我们清除约200MB,这将方便新项目的加载。
我们观察到在一个实例中,如果只调用一次GC.Collect,则其引用仍会在内存中保留一段时间。
GC.Collect();
GC.WaitForPendingFinalizers(); 

但是如果我连续调用两次GC,它会很好地清理一切。

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

非常感谢任何想法或指针。

更新:

类中没有定义终结器。

现在我还考虑了一种情况,即该对象被另一个可能具有终结器的对象引用。 在我们的框架中,只为DBProvider定义了终结器,因此我认为即使是这种情况,也不是问题所在。


8
MMVVVM是什么?罗马编码的一种方式吗? - DanDan
MVVM在WPF中很常见,但在我们的情况下,我们有几个不同的应用程序使用不同的.NET技术,并且它们都基于共同的DomainObject框架,因此我们决定在我们的DomainObjects上面添加一个新的MV层来方便我们的WPF应用程序。 - Nitin Midha
4个回答

12

听起来你的代码里应该有一个终结器(finalizer),如果你只调用一次GC.Collect(),那么这些终结器会被执行,但是终结对象不会被回收。

这是否代表了一个错误就是另外一个问题了。通常情况下,最好没有需要执行终结器的情况,但也许在你的情况下可以接受。


我检查了那个特定的类,据我所见没有finalizers。 - Nitin Midha
现在我正在考虑一种情况,即该对象被另一个可能具有终结器的对象引用。 - Nitin Midha
如果任何对象具有终结器,它直接或间接引用的任何对象将至少部分地在下一次GC中存活(如果有终结器,则将在这些对象上运行;WeakReferences可能会存活,但我不会相信它们总是会)。 - supercat
现在,在另一个地方,即使我调用GC.Collect两次,它仍然没有清理引用。 - Nitin Midha
@Nitin:听起来你还是在引用对象。 - Jon Skeet
显示剩余2条评论

6
如果我只调用一次GC.Collect,它的引用仍然会在内存中存在一段时间。
这并不奇怪。当一个对象有Finalizer(并且没有在其上调用GC.SuppressFinalize()),它会得到执行的机会(它不会被收集,以便Finalizer可以使用有效的对象运行)。此对象引用的所有实例也会得到执行的机会。需要第二轮GC来清理它所有的内容。
另一方面,大多数程序,包括大而复杂的程序,应该能够在没有调用GC.Collect()的情况下运行。而你想要调用两次……
在每个项目生命周期后,我们进行手动Tear Down。
听起来很复杂,但很容易避免……你的Domain/View模型中有多少引用?理想情况下,你只需削减1或2个对“主”对象的引用并忘记它即可。

实际上,整个项目对象都驻留在内存中,包括 V VM MV 和 M 用于项目生命周期,因此我们决定进行手动拆除。同时,我考虑了您提到的情况,请查看 Jon Skeet 回答中的评论。 - Nitin Midha
我仍然不认为需要手动拆除。但这将是复杂的,你只需要错过一个小引用... - H H

0

@Nitin,我不确定我的建议是否能帮到你,但作为一个经验法则,我们应该避免显式调用GC.Collect(),因为它很可能会导致性能问题。相反,尝试遵循适当的处理模式。


0

你有一个弱引用,你期望它被设置为null,因为你调用了GC并且认为没有任何东西使对象保持活动状态。

我不知道在.net中弱引用是如何实现的,但很可能它们会延迟对象的收集,并使用类似于finalizerrs的系统。

或者可能是你有一个需要完成的对象,因为你没有调用dispose()。(这在WPF或WinForms中非常常见)

要找出原因,你需要使用MemoryProfiler,预计至少需要花一天时间学习如何使用任何内存分析器,因为你将看到关于WPF内部对象的大量数据。个人建议下载Redgate's memory profiler的免费试用版,尝试查看问题所在,他们的支持也很有帮助。(其他内存分析器也可以很好地工作,很大程度上取决于你的使用习惯。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接