诊断 RegisterWindowMessage 泄漏
Diagnosing RegisterWindowsMessage leak
我们发现其中一个应用程序的生产服务器上的原子池资源耗尽。
使用出色的 AtomTableMonitor 工具,我们将问题隔离为通过 RegisterWindowsMessage 调用创建大量原子。他们都有这样的名字:
ControlOfs030D000000000270
末尾的数字发生变化。
我的问题是:我们如何确定是哪个进程在创建这些原子?
一些潜在资源:
https://blogs.msdn.microsoft.com/ntdebugging/2012/01/31/identifying-global-atom-table-leaks/
您可以使用 API Monitor 之类的工具并将其设置为仅跟踪 RegisterWindowsMessage
。它将向您显示哪个进程正在使用此函数以及堆栈跟踪(尽管没有符号可能不太有用)。
此外,快速 Google 搜索 ControlOfs
会找到与您的问题相匹配的 https://forums.embarcadero.com/thread.jspa?threadID=47678。他们说这是 VCL 中的错误。如果您有代码,有人发布了此修复包:
http://andy.jgknet.de/blog/bugfix-units/vclfixpack-10/
如果您没有代码,我建议您在生产服务器中查找 Delphi/VCL 应用程序并尝试更新它们或报告问题。
以"ControlOfs..."
开头的原子是由Borland/Embarcadero的VCL(可视化组件库)框架在Delphi/C++Builder中创建的。这些原子实际上是 "ControlOfs<HInstance><ThreadID>"
的形式,其中 <HInstance>
和 <ThreadID>
是十六进制格式(因此,在您的情况下,HInstance = 0x030D0000 = 51183616
、ThreadID = 0x00000270 = 624
)。
还有一个由VCL创建的原子名,形式为"Delphi<ProcessID>"
,其中<ProcessID>
为十六进制格式。
这意味着使用 VCL 的应用程序的每个实例都会创建一个新的唯一 "Delphi..."
原子,并且其主 UI 线程会创建一个新的唯一 "ControlOfs..."
原子(这些原子用于通过 SetProp()
在 VCL 创建的 HWND
中存储 TWinControl
对象指针,供 VCL 的 FindControl()
和 IsDelphiHandle()
实用函数使用)。两个原子都在应用程序启动时注册 GlobalAddAtom()
,并在应用程序关闭时注销 GlobalDeleteAtom()
,因此没有泄漏。
然而,在 Delphi/C++Builder 6 一直到 RADStudio XE2 中,还有另一个原子使用相同的 "ControlOfs..."
名称。此原子是使用 RegisterWindowMessage()
创建的(对于私人 RM_GetObjectInstance
window 消息),无法注销。因此,每当受影响的 VCL 应用 运行 时,都会创建这个唯一的原子并随后泄露。
这最终在 2012 年由 Embarcadero 在 RADStudio XE3 中修复(Andreas Hausladen 为早期的 VCL 版本发布了 patch)。但是,使用旧版本 VCL 编译的现有应用程序会受到影响,如果不修补它们以使用具有 RegisterWindowMessage()
.
的静态名称,您将无法阻止它们泄漏。
因此,为了回答您的问题,结合使用 AtomTableMonitor 和任务管理器,您应该能够弄清楚您正在 运行 哪些应用程序是 VCL 应用程序,然后您可以单独检查它们泄漏的原子。或者,使用 SysInternals Process Monitor 和 Thread Create
过滤器来获取线程 ID 列表及其随时间的创建进程,然后您可以将这些线程 ID 与泄漏的原子名称进行匹配。
我们发现其中一个应用程序的生产服务器上的原子池资源耗尽。
使用出色的 AtomTableMonitor 工具,我们将问题隔离为通过 RegisterWindowsMessage 调用创建大量原子。他们都有这样的名字:
ControlOfs030D000000000270
末尾的数字发生变化。
我的问题是:我们如何确定是哪个进程在创建这些原子?
一些潜在资源:
https://blogs.msdn.microsoft.com/ntdebugging/2012/01/31/identifying-global-atom-table-leaks/
您可以使用 API Monitor 之类的工具并将其设置为仅跟踪 RegisterWindowsMessage
。它将向您显示哪个进程正在使用此函数以及堆栈跟踪(尽管没有符号可能不太有用)。
此外,快速 Google 搜索 ControlOfs
会找到与您的问题相匹配的 https://forums.embarcadero.com/thread.jspa?threadID=47678。他们说这是 VCL 中的错误。如果您有代码,有人发布了此修复包:
http://andy.jgknet.de/blog/bugfix-units/vclfixpack-10/
如果您没有代码,我建议您在生产服务器中查找 Delphi/VCL 应用程序并尝试更新它们或报告问题。
以"ControlOfs..."
开头的原子是由Borland/Embarcadero的VCL(可视化组件库)框架在Delphi/C++Builder中创建的。这些原子实际上是 "ControlOfs<HInstance><ThreadID>"
的形式,其中 <HInstance>
和 <ThreadID>
是十六进制格式(因此,在您的情况下,HInstance = 0x030D0000 = 51183616
、ThreadID = 0x00000270 = 624
)。
还有一个由VCL创建的原子名,形式为"Delphi<ProcessID>"
,其中<ProcessID>
为十六进制格式。
这意味着使用 VCL 的应用程序的每个实例都会创建一个新的唯一 "Delphi..."
原子,并且其主 UI 线程会创建一个新的唯一 "ControlOfs..."
原子(这些原子用于通过 SetProp()
在 VCL 创建的 HWND
中存储 TWinControl
对象指针,供 VCL 的 FindControl()
和 IsDelphiHandle()
实用函数使用)。两个原子都在应用程序启动时注册 GlobalAddAtom()
,并在应用程序关闭时注销 GlobalDeleteAtom()
,因此没有泄漏。
然而,在 Delphi/C++Builder 6 一直到 RADStudio XE2 中,还有另一个原子使用相同的 "ControlOfs..."
名称。此原子是使用 RegisterWindowMessage()
创建的(对于私人 RM_GetObjectInstance
window 消息),无法注销。因此,每当受影响的 VCL 应用 运行 时,都会创建这个唯一的原子并随后泄露。
这最终在 2012 年由 Embarcadero 在 RADStudio XE3 中修复(Andreas Hausladen 为早期的 VCL 版本发布了 patch)。但是,使用旧版本 VCL 编译的现有应用程序会受到影响,如果不修补它们以使用具有 RegisterWindowMessage()
.
因此,为了回答您的问题,结合使用 AtomTableMonitor 和任务管理器,您应该能够弄清楚您正在 运行 哪些应用程序是 VCL 应用程序,然后您可以单独检查它们泄漏的原子。或者,使用 SysInternals Process Monitor 和 Thread Create
过滤器来获取线程 ID 列表及其随时间的创建进程,然后您可以将这些线程 ID 与泄漏的原子名称进行匹配。