我正在编写一个应用程序,在用户使用它之后 - 他可以“重置”它并再次使用它。实际使用会创建一些对象,包括Controls。
我使用Process Explorer检查了内存使用情况,并发现当使用该应用程序时 - 内存使用量有所增加,但当应用程序“重置”时 - 内存使用率几乎没有下降。
因此,在递归时,在所有Dispose
的“重置”I Control
中,甚至添加了GC.Collect();
来启动。但Process Explorer -> (app's-) Properties -> .Net CLR Memory
仍然显示堆上的大量内容和大量内存。
这(必然)是内存泄漏吗?为什么强制GC没有帮助?
编辑:
我添加了GC.Collect();
仅用于调试 - 用于确定是否存在泄漏。在GC工作之前,我并不担心“临时泄漏”,我只是试图找出是否有对象永远不会超出范围(虽然我还没有找到它们)或者它只是一个问题时间,记忆将被回收。
答案 0 :(得分:5)
这(必然)是内存泄漏吗?
嗯......是的,不是。在C#应用程序中,大多数情况下都不可能有真正的内存泄漏,如果从未调用过Dispose(),即使是在引擎盖下使用本机资源的类型也会在终结器中清理它们。
无论如何,有可能有陈旧的引用和本机资源,这些资源需要很长时间才能清理(或者写得不好的本机函数会自行泄漏),这与非托管语言中的真正内存泄漏具有相同的实际效果
为什么强制GC没有帮助?
你给了GC一个建议,它选择不接受它。 GC.Collect()
不会强制GC清理所有内容,文档也会告诉您。其次,这个内存使用是否真的导致了问题?消耗了多少内存?它会被释放吗?如果它只是一小部分,那么你可能不用担心,让GC完成它的工作。
如果没有看到你的代码,我所能做的就是猜测。有几种方法可以在C#应用程序中建立内存压力。
过时的参考资料。列表中您从未清除的“死”对象的引用。事件处理程序也可以导致这种情况。例如,静态事件应始终取消订阅,因为事件委托持有对订阅者的引用。
因此,如果订阅者超出范围而没有取消订阅该事件,则基本上存在内存泄漏,因为始终存在对这些“死”对象的有效引用。请注意,事件的典型用例不会导致这种情况发生,因为父(事件发布者)通常在您需要时超出范围。
不处理实现IDisposable的对象。当然,他们可能会在他们的终结器中清理它们(所有.NET IDisposable类型都会这样做,其余的应该这样做),但终结器在不确定的时间运行,使整个事情变得不可预测。确保尽快调用Dispose()(尽可能使用using
语句)。
大对象堆碎片。如果要分配大量“大”对象(大于85,000字节的对象),则此段内存可能会碎片化。不会像在标准堆上分配的第0和第1代对象那样经常清理此内存。
.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的备注部分),显然只有符合条件的内容才能收集。