【问题标题】:What are the Finalizer Queue and Control+ThreadMethodEntry?什么是终结器队列和 Control+ThreadMethodEntry?
【发布时间】:2010-11-19 02:31:25
【问题描述】:

我有一个似乎泄漏内存的 WindowsForms 应用程序,因此我使用 Redgate 的 ANTS Memory Profiler 查看我怀疑的对象,发现它们仅由 Finalizer Queue 上的对象持有。太好了,究竟什么是终结器队列?你能指出我最好的定义吗?你能分享一些轶事建议吗?

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

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

更新:这个Red Gate article 很有帮助。

【问题讨论】:

    标签: .net winforms memory-leaks finalizer redgate


    【解决方案1】:

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

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

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

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

    System.GC.Collect(); System.GC.WaitForPendingFinalizers(); // 此方法在运行终结器时可能会阻塞 System.GC.Collect();

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

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

    【讨论】:

    • 感谢您的出色回答,保罗。这就是我说的对象引用图,在资源应该被清理之后查看第二个快照中的新对象。图中所有实现 IDisposable 的对象都有一个工具提示,上面写着“已为此对象调用了 Dispose()”,但所选对象没有这样的工具提示。
    • 关于 ThreadMethodEntry 的注意事项:我认为它们用于 UI 线程的任何调用中。每个 Control 对象都有一个 ThreadMethodEntry 类型的线程回调队列。回调使 ThreadMethodEntry 出列并运行它。每个 ThreadMethodEntry 对象都有一堆内部字段。检查这些字段可能会帮助您确定哪些供应商的对象正在调用。我不记得您是否可以从 ANTS 获得该信息,但我知道您可以通过 WinDbg.dll 和 sos.dll(托管调试器扩展)。查看“方法”委托和“调用者”控件。
    • 另外,请注意 ThreadMethodEntry 对象实现了一个终结器,但它们没有 Dispose 方法。当它们完成时,它们也会被移动到终结器队列中。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-27
        • 2021-06-17
        • 1970-01-01
        • 2014-05-16
        • 2012-03-09
        • 1970-01-01
        • 1970-01-01
        • 2021-08-19
        相关资源
        最近更新 更多