调试桌面堆耗尽

Posted

技术标签:

【中文标题】调试桌面堆耗尽【英文标题】:Debugging desktop heap exhaustion 【发布时间】:2018-02-13 12:35:49 【问题描述】:

我目前支持的产品似乎正在消耗大量桌面堆。二进制文件主要是 .net,并且都将会话 0 作为非交互式进程运行(它们都是已安装 Windows 服务的子进程)。因此,据我所知,它们不应该消耗任何桌面堆。

我们有一些环境在系统日志中报告事件 ID 243,然后间歇性地在应用程序日志中报告事件 ID 1000;应用程序日志中的异常始终为 0xc0000142。最终,我们的一项服务也会因一些神秘(无用)的消息而崩溃。不幸的是,我们从未能够捕获到异常,但这些似乎都是桌面堆耗尽的非常明确的指标。

我正在尝试找出消耗这么多桌面堆的原因,以便找出原因。但这是我非常卡住的地方。最初我计划安装 Desktop Heap Monitor,但在尝试让它工作失败后,我意识到它在 XP 之前的任何东西上都不支持。我在某处读到 Process Explorer 应该能够给我相同的信息,所以我们一直在监视 PE 中的以下对象:

    句柄计数 GDI 对象 用户对象

报告事件 243 时的句柄计数值与几天前未发生问题时的处理计数值没有显着差异,甚至在进程启动后的几分钟内也没有显着差异。并且 GDI 和 USER 对象都为零。因此,我不知道究竟是什么耗尽了桌面堆,或者就此而言,如何进一步调试它。我在某处读到 WeakEventManager 可能会导致这个问题,但我们似乎并没有使用它。

我已经在 google 和 SO 上搜索了这个东西,但到目前为止我还没有找到任何东西。我真正想要的是确定哪个进程正在耗尽堆,或者至少哪个进程消耗最多。如果有人对如何执行此操作有任何指示,我将不胜感激。

【问题讨论】:

【参考方案1】:

一个旧线程,但我想我会循环回来,以防将来有人遇到这个问题。经过一些调试,我们确定了导致问​​题的进程。我决定将 WinDbg 附加到该进程并在 CreateWindowEx 和 NtDestroyWindow 上设置一个 bp。果然,确实调用了 CreateWindowEx 来创建隐藏窗口;从堆栈上的参数中,我能够获得该窗口的类(它始终相同),这有助于进一步缩小范围。

随着时间的推移,我开始注意到对 NtDestroyWindow 的调用次数少于对 CreateWindowEx 的调用次数。所以我走下调用堆栈来看看是什么创建了窗口......有一个类构造函数和析构函数(本机,非托管)。似乎我们调用析构函数的频率不如调用构造函数的频率高,所以随着时间的推移,我们泄漏了这些类的一些实例,并且每个实例都“泄漏”了一个隐藏窗口,它随着时间的推移累积并导致桌面堆耗尽问题。从这里,我们设法找到了该类的实例没有被破坏的地方,并且能够解决问题。

但是,我对自己的命运并不满意,我很好奇为什么 Process Explorer 没有像我预期的那样对我有用。一直以来,它显示的用户对象为零,即使我知道该过程正在创建窗口对象。然后我意识到 PE 只能为在同一会话中运行的进程显示此数据。因此,我必须在会话 0 中运行它以跟踪服务的 windowo 对象。在 PsExec 和下面的帖子的帮助下,我能够在会话 0 中运行 PE 并切换到它。

https://superuser.com/questions/426868/interactive-session-0-in-windows-7

https://blogs.technet.microsoft.com/home_is_where_i_lay_my_head/2012/10/09/windows-8-interactive-services-detection-error-1-incorrect-function/

从那里,我确实可以看到该进程有超过 1,000 个用户对象。我还能够运行 WinSpy 并确认我的发现。当然,在这个阶段,这都是学术性的,但也许这对将来的某人有用。

【讨论】:

以上是关于调试桌面堆耗尽的主要内容,如果未能解决你的问题,请参考以下文章

WinXP 上的桌面堆监视器 (dheapmon.exe) 没有数据

比较 Java 内存堆转储:Java 桌面应用程序的内存分析 [关闭]

如何在 Windows Azure 中使用远程桌面调试辅助角色?

在vs中进行qt桌面应用开发时,编译器堆溢出的编译错误(error C1060编译器堆内存不足)

在vs中进行qt桌面应用开发时,编译器堆溢出的编译错误(error C1060编译器堆内存不足)

如何在Android Studio中调试“桌面”libgdx应用程序