奇怪的句柄泄漏。

13
我的应用程序(基于MFC与C++/CLI的互操作,但也包含大量C#、Windows Forms和WPF)存在句柄泄漏。应用程序启动后不久,我可以看到任务管理器中的句柄计数持续增长(每秒增加10个新句柄)。因此,我使用 handles.exe 查看它们是哪种句柄。我发现正在泄漏的句柄是进程句柄。而且它们是指向我的应用程序进程的进程句柄。
所以我想知道通常会创建指向运行进程的句柄的操作是什么。有什么想法吗?你见过这样的情况吗?考虑到我不能使用调试DLL并且只能使用可以进行xcopy部署的工具,还能做些什么来跟踪泄漏?
更新:
我能够使用和!handle, !htrace追踪到进程句柄都是使用以下堆栈跟踪创建的(按频率排序):
0x79f7570b: mscorwks!CorExitProcess+0x00022055
0x79f03edd: mscorwks!GetPrivateContextsPerfCounters+0x0000b6fe
0x79f04b87: mscorwks!GetPrivateContextsPerfCounters+0x0000c3a8
0x79f04b03: mscorwks!GetPrivateContextsPerfCounters+0x0000c324
0x79f919bf: mscorwks!CorExitProcess+0x0003e309
0x79f91b28: mscorwks!CorExitProcess+0x0003e472
0x792d6b4c: mscorlib_ni+0x00216b4c
0x1391a663: +0x1391a663
0x1391a0b1: +0x1391a0b1
0x7a9ea544: System_ni+0x005aa544
0x792a842f: mscorlib_ni+0x001e842f
或者
0x7c8106f5: kernel32!CreateThread+0x0000001e
0x79f04bb2: mscorwks!GetPrivateContextsPerfCounters+0x0000c3d3
0x79f04b03: mscorwks!GetPrivateContextsPerfCounters+0x0000c324
0x79f919bf: mscorwks!CorExitProcess+0x0003e309
0x79f91b28: mscorwks!CorExitProcess+0x0003e472
0x792d6b4c: mscorlib_ni+0x00216b4c
0x1391a663: +0x1391a663
0x1391a0b1: +0x1391a0b1
0x7a9ea544: System_ni+0x005aa544
0x792a842f: mscorlib_ni+0x001e842f
或者
0x08ec2eba: +0x08ec2eba
0x792b8277: mscorlib_ni+0x001f8277
0x792b8190: mscorlib_ni+0x001f8190
0x792b8040: mscorlib_ni+0x001f8040
0x792b7ff2: mscorlib_ni+0x001f7ff2
0x677e48f3: System_Runtime_Remoting_ni+0x000748f3
0x677e44be: System_Runtime_Remoting_ni+0x000744be
0x677e46ec: System_Runtime_Remoting_ni+0x000746ec
0x677e8408: System_Runtime_Remoting_ni+0x00078408
0x7926eb8d: mscorlib_ni+0x001aeb8d

那告诉我什么?


它是否持续增长?即它是否真的是一个泄漏,还是运行时正在启动? - Nick
它会不断增长到几百万个句柄,直到最终崩溃。 - bitbonk
3
托管代码调用栈呢?尝试使用 !clrstack(SOS 调试器扩展中可用)查看它们是否能为你提供有关这些句柄被创建的线索。编辑:我不知道你是否能够直接从句柄中获取托管调用栈,但你仍然可以在 kernel32!CreateThread 或 kernel32!CreateProcessEx 函数中设置断点,从那里获取托管堆栈。 - floyd73
1
@bitbonk:你尝试在“OpenProcess”处设置断点了吗?它被触发过吗? - user541686
2
当应用程序最终崩溃时,您是否有异常处理程序或事件日志中的消息?这可能会为您提供更多信息。 - Kaido
显示剩余8条评论
3个回答

5
调用堆栈看起来不对。你有正确设置符号服务器吗?在Windbg中使用.symfix应该能解决这个问题。然后你应该能够得到更好的堆栈跟踪。
看起来具有此问题的代码部分是托管代码,因此在DuplicateHandle和OpenProcess上中断并转储托管调用堆栈是有意义的。这两种方法是唯一可能生成真实进程句柄的方法。
你可以像这样声明一个断点并在断点被命中时执行命令。在这种情况下,将打印托管堆栈,然后继续执行。
bp kernel32!OpenProcess "!ClrStack;g"

3
方法后面的数字非常高。没有任何一个方法长达几百个字节。如果你看到偏移量在千位或更高,那么这表明你可能有错误或缺失的符号。调试器会使用模块中最近的导出方法作为备选项,以至少显示一些内容。 - Alois Kraus

1

我曾经遇到过通过互操作调用COM对象的Web服务问题。

我解决了这个问题,方法是显式地调用Marshal.ReleaseComObject来释放我创建的互操作对象。之后我就没有遇到过任何问题。

希望能对你有所帮助。


0

那么...你是否在显式地使用性能计数器(如果是的话,请尝试禁用它们以缩小泄漏源范围)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接