我正在追踪我们的MFC代码中出现的问题,看起来像是资源限制问题。内存和CPU看起来都很好。根据任务管理器上的进程选项卡,我们的GDI对象看起来符合其他应用程序的要求,但是我们的USER对象似乎比其他应用程序高出10倍。
"USER对象"是什么,以及它们有哪些限制?
这是一篇“经典”的 MSDN 文章:给我一个句柄,我将给你展示一个对象
上次我在跟踪 Windows 对象泄露(我猜想你也有这个问题),Process Explorer非常方便。它的下面板可以显示一些已分配的系统对象,并且可以对简单的 USER、GDI 等对象进行计数。
桌面堆栈是一个内存池,实际上是句柄所代表的“东西”所在(至少是一些句柄,至少不包括内核句柄)。有时候不仅仅是要关注您已经分配了多少句柄,而更应该考虑每个句柄下面的 对象 使用了多少内存。您可以通过这种方式来调试堆栈。它是痛苦的过程,需要安装。
点击这里可以了解更多详情:
对象类别
系统提供三种对象类别:用户、图形设备接口(GDI)和内核。系统使用用户对象来支持窗口管理,使用 GDI 对象来支持图形,而使用内核对象来支持内存管理、进程执行和进程间通信(IPC)。如需有关创建和使用特定对象的信息,请参阅相关概述。
点击这里可以了解更多详情:
用户对象
用户界面对象每个对象仅支持一个句柄。进程无法继承或复制用户对象的句柄。在一个会话中的进程无法引用另一个会话中的用户句柄。
每个会话理论上最多可以有 65,536 个用户句柄。但实际上,每个会话打开的用户句柄数通常较低,因为它受可用内存的影响。每个进程还有一个默认的用户句柄限制。若要更改此限制,请设置以下注册表键值:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERProcessHandleQuota
该值可以设置为200到18,000之间的数字。
马克·鲁辛诺维奇是该主题的专家。他写了书籍《Windows Internals》。幸运的是,不仅有这本书,还有一篇2019年6月27日发布的文章。
首先,他描述了用户对象:
USER对象得名于它们代表用户界面元素,例如桌面、窗口、菜单、光标、图标和加速表(菜单键盘快捷方式)。
然后继续描述限制:
窗口管理器施加的一个基本限制是,任何进程都不能创建超过10,000个USER对象。
以及
关于USER对象数量的一个根本限制源于其标识符在最初的16位Windows版本中是16位值。当32位支持在以后的版本中添加时,USER标识符必须保持限制为16位值,以便16位进程可以与由32位进程创建的窗口和其他USER对象进行交互。因此,在会话上可以创建的USER对象总数的限制为65,535(2^16)。
他还解释了由@Tobi提到的数字:
出于历史原因,窗口必须具有偶数标识符,因此每个会话最多可以有32,768个窗口。
我不知道它们是什么,但我知道它们包括窗口句柄。
对于窗口句柄,系统范围内最大约为32000,每个进程的最大值为10000。(这可能只是用户对象限制,而不仅仅是窗口句柄。)
如果你在某种方式下泄漏窗口句柄,或者使用大量的窗口,那么窗口句柄的数量可能非常高。(请注意,即使像文本标签这样的简单控件也会消耗一个窗口句柄。)