什么是Finalizer Queue和Control + ThreadMethodEntry?

时间:2009-08-12 20:25:43

标签: .net winforms memory-leaks finalizer redgate

我有一个看似泄漏内存的WindowsForms应用程序,因此我使用Redgate的ANTS Memory Profiler来查看我怀疑的对象,并发现它们仅由 Finalizer Queue 上的对象保留。很棒,究竟什么是Finalizer Queue?你能指出我最好的定义吗?你能分享任何轶事建议吗?

此外,Finalizer Queue上的所有根GC对象都是名为“caller”的 System.Windows.Forms.Control + ThreadMethodEntry 对象的实例。我看到它涉及多线程UI交互,但除此之外我不太了解。原谅我明显的懒惰并承认无知,但这些资源都埋藏在供应商的组件中。我正在和供应商讨论这些问题,但我需要一些指导才能让我加快对话速度。你能指点我最有用的ThreadMethodEntry定义吗?任何轶事建议?

另外,我是否应该关注终结器队列中的这些对象?

更新:Red Gate article非常有用。

3 个答案:

答案 0 :(得分:16)

终结器队列包含定义了终结器方法的所有对象。回想一下,终结器是一种收集非托管资源(如句柄)的方法。当垃圾收集器收集垃圾时,它会将带有终结器的任何对象移动到终结器队列中。在某些时候 - 取决于内存压力,GC启发式和月亮阶段 - 当垃圾收集器决定收集这些对象时,它会沿着队列走下去并运行终结器。

过去曾经处理过内存泄漏,在终结器队列中看到一堆供应商的对象可能是草率的代码,但它并不表示内存泄漏。通常,良好的代码将公开一个Dispose方法,该方法将收集托管和非托管资源,并且这样做会通过GC.SuppressFinalize()将自己从终结器队列中删除。因此,如果供应商的对象确实实现了Dispose方法,并且您的代码没有调用它,那么可能会导致终结器队列中出现一堆对象。

您是否尝试过在两个时间点之间在ANTS中创建快照并比较它们之间创建的对象?这可以帮助您识别泄露的任何托管对象。

此外,如果您想查看终结器运行时内存是否消失,请尝试使用以下内容进行测试:

System.GC.Collect();
System.GC.WaitForPendingFinalizers(); // this method may block while it runs the finalizers
System.GC.Collect();

我不建议正常运行此代码。如果你刚刚做了大量的工作并且创造了大量的垃圾,你可能想要运行它。例如,在我们的应用程序中,我们的一个函数可以创建大约350 MB的垃圾,在关闭MDI窗口后会浪费掉。由于已知会留下大量垃圾,我们会手动强制进行垃圾回收。

另请注意,基本Windows.Forms代码中有一个低级属性缓存,它将保留最后打开的模式对话框。这可能是内存泄漏的原因。摆脱此引用的一种可靠方法是强制显示另一个简单对话框,然后运行上面的GC代码。

答案 1 :(得分:1)

终结器队列是一个队列,其中不再使用的对象实例正在等待GC最终确定。此队列中的所有对象都将被最终确定,并且您的内存泄漏可能不会直接来自其中一个。但是,其中一个对象可能无法释放所有非托管资源。

ThreadMethodEntry类是IAsyncResult的实现,并且通常在调用异步操作时创建此类的实例,例如使用Invoke更新UI或使用Begin * / End *方法。

答案 2 :(得分:0)

Here's一篇描述类似问题的好博客文章。在技​​术层面上,您可以使用SOS.dll(博客文章描述)和Sosex.dll来帮助您找出这些ThreadMethodEntry对象在内存中闲置的原因。这些WinDbg扩展中的命令可以跟踪其他对象引用内存中的特定对象。