我看到的是真正的内存泄漏吗?

时间:2012-10-30 21:32:12

标签: c# .net winforms

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

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

因此,在递归时,在所有Dispose的“重置”I Control中,甚至添加了GC.Collect();来启动。但Process Explorer -> (app's-) Properties -> .Net CLR Memory仍然显示堆上的大量内容和大量内存。

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

编辑:

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

3 个答案:

答案 0 :(得分:5)

  

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

嗯......是的,不是。在C#应用程序中,大多数情况下都不可能有真正的内存泄漏,如果从未调用过Dispose(),即使是在引擎盖下使用本机资源的类型也会在终结器中清理它们。

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

  

为什么强制GC没有帮助?

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

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

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

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

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

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

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

    当我用尽其他途径时,我使用RedGate's .NET memory profiler获得了很好的体验。我不是为他们工作,也不是我以任何方式与他们有关联,所以你知道,他们也有免费试用。值得一看。

答案 1 :(得分:2)

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

What strategies and tools are useful for finding memory leaks in .NET?

.NET Framework中的垃圾收集器在内存压力超过某种启发式之前不会释放内存。

答案 2 :(得分:1)

正如其他人已经注意到GC.Collect()单独不能确保GC,你可以尝试的一件事是GetTotalMemory方法,它采用一个布尔参数,允许你强制一个完整的GC,例如。

GC.GetTotalMemory(true);

这将强制进行总计收集,然后以分配的字节为单位给出近似的托管内存量。

与显示的其他方法一样,保证集合发生(请参阅MSDN documentation的备注部分),显然只有符合条件的内容才能收集。