5

我目前调试一个多线程应用程序,它运行没有错误,直到某些函数被调用大约 2000 次。之后应用程序停止响应,我可以追踪到 _beginthreadex 因内存不足错误而失败。

在 ProcessExplorer 中检查应用程序时,我可以看到越来越多的线程句柄泄漏和不断增长的虚拟内存,直到发生错误,私有字节保持低位。泄露的线程也调用 CoInitialize 并且从不调用 CoUninitialize。

我想知道的是:

  • 虚拟内存代表什么?
  • 虚拟内存是否与泄漏的线程句柄有关?
  • COM 或 MSXML6(由线程调用)是否复制线程句柄以及如何关闭它们?

我希望我的问题很清楚并且不会破坏任何规则,这是我的第一个问题,英语不是我的第一语言。:-(

我忘了提一下,一旦线程终止,我就会关闭 _beginthreadex 返回的句柄,这将打开的句柄数量减少了大约一半,但不会影响虚拟内存。此外,在我插入 CloseHandle 调用之前,ProcessExplorer 中显示的每个线程句柄的线程句柄计数为 2。

编辑

我以前不包括这个而变得愚蠢,我知道线程退出是因为在使用 Visual Studio 进行调试时活动线程的数量没有增加。而且我确实希望并非所有泄漏的内存都是调用 TerminateThread 的结果,因为它们在一个相当大的库中使用,我不想修改它。

对于我的问题的 com 部分,使用 !htrace -diff 我发现由 msxml 分配但在函数调用结束后未释放的线程句柄,它们可能与泄漏有关还是会在以后关闭?

感谢所有这些评论,虽然问题仍然存在,但他们帮助我更好地理解它。

4

2 回答 2

4

一个进程可用的虚拟内存是 4Gb 地址空间中的 2Gb。默认情况下,每个 Thread 为其堆栈空间保留大约 1Mb 的虚拟内存空间。因此,在虚拟内存耗尽之前,win32 应用程序有大约 2000 个活动线程的限制。

虚拟内存是应用程序在现代虚拟内存操作系统(如 Windows)中获得的内存。在 Win32 上发生的情况是,您的应用程序获得了 2Gb 的虚拟地址空间。当您的程序调用 new 或 malloc 时,在穿过几层隧道之后,会在页面文件中为您的应用程序在磁盘上分配空间。当 CPU 指令尝试访问该内存时,会引发硬件异常,内核会为该区域分配物理 RAM,并从页面文件中读取内容。因此,无论 PC 中的物理 RAM 是多少,每个应用程序都认为它可以访问整个 2Gb。虚拟内存是您的 2Gb 空间已用完多少的计数。

每个线程(见上文)保留 1 Mb 的虚拟地址空间以供其堆栈增长。大多数 1Mb 只是保留空间(希望如此),没有 RAM 或页面文件的支持。

当您关闭线程句柄时,您不会关闭线程。线程由另一个调用 TerminateThread 的线程终止(这会泄漏线程堆栈和一些其他资源,因此永远不要使用它)、调用 ExitThread() 本身或退出它们的 ThreadProc。

因此,对于 2000 次调用限制、不匹配的 CoInitialize 和 CoUninitialize 调用,我会说您的线程没有干净地退出或根本没有退出。2000 个工作线程中的每一个都卡在做某事而不是在完成工作后退出。

于 2009-11-21T17:48:42.220 回答
3

/函数不会自动关闭线程句柄,所以必须调用win32_beginthreadex函数来关闭它。句柄是返回的值。如果您使用/代替,句柄将自动关闭。_endthreadexCloseHandle_beginthreadex_beginthread_endthread

关于虚拟内存:它表示已保留但尚未使用的内存。泄漏的内存(或至少一部分)与句柄泄漏有关。当线程的最后一个句柄关闭时,Windows 将释放为其保留的虚拟内存。

于 2009-11-21T16:15:13.610 回答