什么是Finalizer Queue和Control + ThreadMethodEntry?

fli*_*ubt 7 .net memory-leaks finalizer redgate winforms

我有一个WindowsForms应用程序,似乎泄漏内存,所以我使用Redgate的ANTS内存分析器来查看我怀疑的对象,并发现它们只由已经在Finalizer Queue上的对象持有.很棒,究竟什么是Finalizer Queue?你能指出我最好的定义吗?你能分享任何轶事建议吗?

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

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

更新:这篇红门文章很有帮助.

Pau*_*ams 17

终结器队列包含定义了终结器方法的所有对象.回想一下,终结器是一种收集非托管资源(如句柄)的方法.当垃圾收集器收集垃圾时,它会将带有终结器的任何对象移动到终结器队列中.在某些时候 - 取决于内存压力,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代码.

  • 关于ThreadMethodEntry的注意事项:我认为它们在UI线程的任何Invoke中使用.每个Control对象都有一个ThreadMethodEntry类型的线程回调队列.回调使ThreadMethodEntry出列并运行它.每个ThreadMethodEntry对象都有一堆内部字段.检查这些字段可能有助于您确定这些供应商的哪些对象正在调用.我不记得你是否可以从ANTS获得该信息,但我知道你可以通过WinDbg.dll和sos.dll(托管调试器扩展).查看"方法"委托和"调用者"控件. (2认同)