3

我正在编写一个应用程序,在用户使用它之后 - 他可以“重置”它并再次使用它。实际使用会创建一些对象,包括控件。

我使用 Process Explorer 检查了内存使用情况,发现当使用该应用程序时 - 内存使用量有所增加,但当应用程序“重置”时 - 内存使用量几乎没有下降。

Dispose因此,在所有s的“重置”时,我Control递归地,甚至添加了一个GC.Collect();启动。但Process Explorer -> (app's-) Properties -> .Net CLR Memory仍然在堆上显示了很多东西,并且使用了很多内存。

这(必然)是内存泄漏吗?为什么强制 GC 没有帮助?

编辑:

我添加了GC.Collect();仅用于调试 - 用于确定是否存在泄漏。在 GC 工作之前,我并不担心“临时泄漏”,我只是想找出是否有对象永远不会超出范围(尽管我还没有找到它们),或者这只是一个问题时间,内存将被回收。

4

3 回答 3

5

这(必然)是内存泄漏吗?

嗯……是的,不是的。在 C# 应用程序中,大部分情况下不可能发生真正的内存泄漏,如果从未调用过 Dispose(),即使在后台使用本机资源的类型也会在其终结器中自行清理。

无论如何,可能有陈旧的引用和本机资源需要很长时间才能清理(或编写不当的本机函数会泄漏自身),这与非托管语言中真正的内存泄漏具有相同的实际结果。

为什么强制 GC 没有帮助?

你给了 GC 一个建议,它选择不接受。GC.Collect()不会强制 GC 清理所有内容,文档也会告诉您。其次,这种内存使用真的会导致问题吗?消耗了多少内存?它会被释放吗?如果只是少量,那么您可能什么都不担心,让 GC 完成它的工作。

在没有看到您的代码的情况下,我所能做的就是猜测。有几种方法可以在 C# 应用程序中建立内存压力。

  1. 陈旧的参考资料。对您永远不会清除的“死”对象的引用列表。事件处理程序也可能导致这种情况。例如,应始终取消订阅静态事件,因为事件委托持有对订阅者的引用。

    因此,如果订阅者在没有取消订阅事件的情况下超出范围,则本质上存在内存泄漏,因为始终存在对这些“死”对象的有效引用。请注意,事件的典型用例不会导致这种情况发生,因为父级(事件发布者)通常会在您需要时超出范围。

  2. 不处理实现 IDisposable 的对象。当然,它们可能会在它们的终结器中自行清理(所有 .NET IDisposable 类型都会这样做,其余的应该),但是终结器在不确定的时间运行,这使得整个事情变得不可预测。确保尽快调用 Dispose()(尽可能使用using语句)。

  3. 大对象堆碎片。如果您要分配大量“大”对象(> 85,000 字节的对象),那么这部分内存可能会变得碎片化。此内存不会像在标准堆上分配的第 0 代和第 1 代对象那样经常清理。

.NET GC 很复杂,而且通常非常擅长它的工作。有时调用 是可以接受的GC.Collect,但您应该了解何时这是一个好主意,并意识到它可能会或可能不会按照您的意愿行事。例如,如果您创建了大量的短期对象。

当我用尽其他途径时,我在使用RedGate 的 .NET 内存分析器时获得了很好的体验。我不为他们工作,也不以任何方式与他们有任何关系,正如你所知,他们也有免费试用。值得一看。

于 2012-10-30T21:40:59.407 回答
2

要查找泄漏的内存,请使用专门为此设计的工具:

哪些策略和工具可用于查找 .NET 中的内存泄漏?

.NET Framework 中的垃圾收集器不会释放内存,直到内存压力超过某个启发式。

于 2012-10-30T21:40:06.447 回答
1

正如其他人GC.Collect()单独指出的那样并不能确保 GC,您可以尝试的一件事是GetTotalMemory采用布尔参数的方法,允许您强制执行完整的 GC,例如

GC.GetTotalMemory(true);

这将强制进行总收集,然后为您提供分配的托管内存量的近似值(以字节为单位)。

与显示的其他方法一样,这并不能保证会发生收集(请参阅MSDN 文档的备注部分),并且显然只能收集符合条件的东西。

于 2012-10-30T21:45:22.530 回答