可能由于 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
...
我们在数百个客户端中安装了 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
...