破坏垃圾收集器?

时间:2009-08-10 21:46:04

标签: .net garbage-collection

好的,这可能是一个noob问题,但现在可以了。

是否有可能“超出”垃圾收集器?

我问的原因是因为我有一个递归方法,在我的应用程序中的一些事件中运行了几次(通过几个我的意思是每秒大约60次,并且事件持续无限的时间,甚至几分钟) 。问题是在递归方法的每个循环中,我创建了一个相当复杂的对象(TreeViewItem包含我们需要的一些mod用于项目和另一个复杂的对象,它作为TreeViewItem的DataContext),所以我担心的是这些TreeViewItems停留在递归方法运行时在堆中,所以当垃圾收集器启动时,它不会清除它们;并且可能,下一次递归方法启动时,它会堆叠更多的TreeViewItem,垃圾收集器永远不会赶上。

问题是我们有内存泄漏,我们正在寻找罪魁祸首。

任何真正有用的帮助

8 个答案:

答案 0 :(得分:7)

不,你不能通过重载来使垃圾收集器泄漏。

垃圾收集器的工作方式实际上可以很好地处理你的情况。

如果删除对大型对象的引用,则不会一次收集一个对象。它看到没有任何对象具有任何活动引用,它将立即收集整组对象。

大多数对象都是短暂的,因此垃圾收集器可以有效地处理它。例如,如果您已填充第一代堆,并且要收集90%的对象,则垃圾收集器不会删除90%。相反,它将10%移动到下一代,只需擦除第一代。

如果垃圾收集器仍然发现自己需要做很多工作,它就会这样做,你将不得不等待它。它会在线程工作时冻结它们,并且它们在垃圾收集器完成之前就不会运行。

答案 1 :(得分:4)

不,你不能'超越'垃圾收集器,它会在必要时中断你的代码。详细信息取决于您是否作为服务器运行。

更大的问题是:为什么你认为你有内存泄漏?这就是你开始寻找原因的地方。

请不要调用GC.Collect(),它会带来更多弊大于利。


加法1

内存管理可能发生的主要“问题”是保持您周围的引用不再需要。通常情况下,流程会非常自然,因为当方法返回时会清除局部变量。但是你应该寻找在对象之间建立的引用。一个值得注意的例子是事件处理程序,如果您的对象订阅了一个事件,那么该订阅包含对订阅对象的引用。确保在不再需要时清除它们。

答案 2 :(得分:1)

垃圾收集者会看堆栈吗?

我使用的只关注堆,如果必要的话可以阻止所有线程,同时它们会乱扔堆。

你有什么证据证明你有泄漏而不是一般的东西太多了?有时堆栈会朝着一个方向生长而堆积在另一个方向 - Out Of Memory并不意味着“泄漏”。

答案 3 :(得分:1)

我很抱歉不同意,但可以超越.Net垃圾收集器 - 至少是并发版本(服务器显然同步运行)。

我已经看到这种情况发生在我的一个WinForms应用程序循环中,分配大量内存删除引用。

我写了一个简单的测试用例证明了这一点。在VB中:

while true
   dim foo(4500000) as byte ' allocate 4.5 MB
   foo = nothing ' c# null - remove ref
end while

在我的应用程序中(必须分配大量对象,因为您可以看到GC线程在Process Explorer中努力工作),最终会发生OutOfMemory异常。在一个空白的虚拟应用程序中,这将导致异常,因为GC可能非常快速地完成其工作。你可以摆弄内存块的大小,最终使它失败。

编辑:显然,如果您要重现上述内容,则需要以x86而不是x64运行,否则您可能需要等待一段时间......

答案 4 :(得分:0)

我不相信;垃圾收集器通常隐含地绑定到内存分配中;因此可以在分配内存时调用;但是,我不是.Net框架如何进行垃圾收集的专家。

内存泄漏的最可能原因(在托管代码中)是您已完成的对象;但仍然可以参考。

答案 5 :(得分:0)

GC仅在检测到需要运行时才会运行。换句话说,当它检测到有足够的内存压力要求收集周期时,通常(虽然这不是唯一的原因)是没有足够的堆空间来执行下一次分配。

你需要寻找的一些东西是在你的循环中使用一次性对象而不是事后处理它们(应该在using块中完成)并在循环中执行大量的字符串连接。

您需要确保正确结束递归并升级回调用堆栈。此外,您是否需要为每个TreeViewItem设置DataContext?这是可以在TreeView上设置一次并让TreeViewItems遍历层次结构以找到适当的DataContext吗?

答案 6 :(得分:0)

如果这是你的问题:

  

每次运行此方法时,都会   记忆力不断上升   程序员发现它能够   之后达到OutOfMemoryException   达到1.5 GB(半个   小时)。

然后我建议一次一层地从一个层中剥离函数(在有意义的层中),直到你得到一些不会丢失内存的东西。然后将功能添加到一些小小的碎片中,直到找到导致泄漏的功能。

答案 7 :(得分:0)

你不能“超越”垃圾收集器,但它可能会严重压力。这通常表现为高CPU使用率,而不是GC不清理需要清理的大块内存。

如果你在半小时内构建一个OutOfMemoryException,那么肯定不是你的垃圾收集器超载了。正如其他人所说,它更有可能在您运行应用程序时,对您正在创建的新对象的引用仍然存在。这并不意味着你应该只是将你的计时器循环设置为null,你需要确定保留引用的原因以及你应该采取什么行动来整理你工作结束时的事情。目前还没有。

这种工作的最佳工具是内存分析器。他们将在您的应用程序中分析生物对象及其对它们的引用,并让您找出您认为应该收集哪些对象仍然存在,以及为什么。这将是您泄漏的原因。

GC将确定应用程序中哪些对象保证存活(gc根),例如静态变量,局部变量等。然后它将遵循根引用以查找每个当前生存的对象。剩下的就是垃圾。这是一种非常简化的放置方式,但它表明您需要找到的是根路径导致您正在创建的对象,并在适当的位置打破它们。

虽然你说你自己并没有使用事件,但很可能一直存在的一些控件是订阅新对象上的事件,或者将它们保存在某个列表中。

我之前使用过很多Scitech memory profiler,发现它非常有帮助。还有其他工具 - dotTraceAnts memory profiler等等。我已经使用了其中的一些,并发现Scitech的分析器达到了最佳状态。他们可能都有试用期! :)