可能由于 Finalizer 线程故障导致的内存泄漏故障排除

Troubleshooting memory leak possibly due to Finalizer thread malfunction

我们在数百个客户端中安装了 WinForms 应用程序。在其中一些中,内存使用量开始随着时间的推移而明显无缘无故地上升,直到最终抛出 OutOfMemoryException。

我们使用 DebugDiag 收集和分析来尝试了解问题。我们得到了 these warnings.

所以,首先要看的是终结器线程,因为有一个巨大的对象队列 (42K) 准备终结。这是它的调用堆栈在转储时的样子:

ntdll!KiFastSystemCallRet
ntdll!NtWaitForSingleObject+c
KERNELBASE!WaitForSingleObjectEx+98
kernel32!WaitForSingleObjectExImplementation+75
clr!CLREventBase::Reset+17e
clr!CLREventBase::Reset+1c6
clr!CLREventBase::WaitEx+152
clr!FinalizerThread::WaitForFinalizerEvent+38
clr!FinalizerThread::FinalizerThreadWorker+5f
clr!Thread::DoExtraWorkForFinalizer+1b1
clr!Thread::DoExtraWorkForFinalizer+234
clr!Thread::DoExtraWorkForFinalizer+5f8
[[DebuggerU2MCatchHandlerFrame]]
clr!ManagedThreadBase::FinalizerBase+33
clr!FinalizerThread::FinalizerThreadStart+d4
clr!Thread::intermediateThreadProc+55
kernel32!BaseThreadInitThunk+e
ntdll!__RtlUserThreadStart+70
ntdll!_RtlUserThreadStart+1b

相同的过程,五个小时后,DebugDiag 显示的警告是 these。

因此,据称泄漏的内存几乎翻了一番,因为准备完成的对象数量显着增加。 Finalizer 线程调用堆栈与前一个相同。事实上,问题似乎出在阻塞的终结线程上,导致 GC 无法清除未使用的对象。

事实是,我们不在代码中使用终结器。终结器线程可能正在执行什么阻塞操作?我们怎么才能知道是否有一个外部模块负责这个,它是哪个?

一些额外信息:我们使用的是 .NET Framework 4.0,我们的应用程序是为 x86 编译的。该问题更有可能在 Windows 7+ SO 中重现,而 XP 似乎表现更好。

任何有关查找位置的帮助或提示都将不胜感激。

物有所值: 几年前,我有过类似的情况。当您的程序耗尽资源时,gdi 错误是典型的。最后用redgate的Ants(免费试用期)发现内存中保存了很多对象。我使用了 VB.NET,这是一个鲜为人知的事实,即使用 'withevents' 声明的变量需要在终结器中显式设置为 nothing。传递和保留对其他对象的引用也是垃圾人没有出现的潜在原因。在终结器中将这些引用设置为空(null)会有所帮助。在 VB 中,表单的终结器代码在设计器文件中:

 'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
        End If

        MyBase.Dispose(disposing)
        If Me.DesignMode Then Exit Sub

        If disposing Then
            JCboKind.DataSource = Nothing
            JCBOCondition.DataSource = Nothing
            JCboQuality.DataSource = Nothing
            JCboSafetyCategory.DataSource = Nothing
            JcboSurfTreatment.DataSource = Nothing
            JCboUnit.DataSource = Nothing
        End If
        ...