使用 UMDH 之前
如果您认为您遇到内存泄漏, 应注意, 内存泄漏可能不它们似乎什么。 您可能发现内存泄漏是不满足内存泄漏, 但是性能增强。 例如, MicrosoftJet 数据库引擎会消耗大量内存因为它检索数据并写入缓存 256 - MB 计算机上 (最多 128 MB)。 缓存允许 Jet 引擎以获得快速读和写提前缓冲。
要确定是否是一个进程遇到内存泄漏, 使用 Windows 性能监视器 (Perfmon.exe) 并为应用程序进程类别下监视专用字节。 专用字节是总内存, 进程已分配, 但不是与其他进程共享。 注意这是不同于 VirtualBytes, 即有趣来监视。 虚拟字节是以字节表示的虚拟地址空间, 进程使用当前大小。 应用程序可泄漏虚拟内存, 但可能没有看到分配专用字节中差别。 如果看不到当监视专用字节, 但怀疑仍然运行用尽了内存, 增加内存监视虚拟字节以查看备份虚拟内存是否正在使用。 有关检测内存泄漏, 附加信息请参阅以下 Microsoft Developer Network (MSDN) Web 站点上 " 查找 Leaks 和瓶颈与 WindowsNT PerfMon COM 对象 " 文章:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnperfmo/html/perfmon.asp
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnperfmo/html/perfmon.asp)
要确保应用程序, 是泄漏内存, 可疑代码放在循环与许多迭代, 并然后监视对任何提高的内存和虚拟专用字节。 观看以确保不的专用字节和虚拟字节数不最终保持相同, 并且停止数增加。 如果没有内存增加, 停止时间点看不到内存泄漏但更可能, 到缓存, 增长至其最大尺寸 (例如, 它并不继续到 climb 无限)。
如果您决定使用 UMDH 之前,, 您看到内存泄漏, 请按照下列步骤:
1. |
安装 UMDH 实用程序。 |
2. |
设置要安装 UMDH 文件夹 PATH 系统环境变量。 |
3. |
将 _ NT _ SYMBOL _ PATH 环境变量设置为 Microsoft 符号服务器路径以便 UMDH 可以找到调试符号文件。 |
UMDH 工具是随 DebuggingToolsforWindows 产品下面的 Microsoft Web 站点:
http://www.microsoft.com/whdc/devtools/ddk/default.mspx
(http://www.microsoft.com/whdc/devtools/ddk/default.mspx)
下载和安装实用程序, 并将 PATH 系统环境变量到路径其中已安装调试工具。
使用 UMDH 之前, 必须安装用于组成应用程序和操作系统正确调试符号。 用于 MicrosoftSymbolServer 获取调试符号对于 Microsoft 组件。 有关 MicrosoftSymbolServer, 请单击文章编号以查看 Microsoft 知识库中相应:
311503
(http://support.microsoft.com/kb/311503/EN-US/) 使用 Microsoft 符号服务器来获取调试符号文件
UMDH 尝试通过使用 _ NT _ SYMBOL _ PATH 环境变量查找符号文件。 命令以从命令提示符设置路径可能与以下类似:
设置 _ NT _ SYMBOL _ PATH = SRV c:\LocalSymbolCache *
其他有关设置符号调试信息, 请参阅本文中后面 " 调试符号 " 部分。
在完成这些步骤, 您就可以使用 UMDH 实用程序。
与 UMDH 转储捕获堆
UMDH 是实用工具, 转储堆分配的进程信息。 此信息包括调用堆栈用于每个分配、 次数, 通过调用该堆栈, 进行分配和的消耗, 通过调用该堆栈字节数。 例如:
00005320 bytes in 0x14 allocations (@ 0x00000428) by: BackTrace00053
ntdll!RtlDebugAllocateHeap+0x000000FD
ntdll!RtlAllocateHeapSlowly+0x0000005A
ntdll!RtlAllocateHeap+0x00000808
MyApp!_heap_alloc_base+0x00000069
MyApp!_heap_alloc_dbg+0x000001A2
MyApp!_nh_malloc_dbg+0x00000023
MyApp!_nh_malloc+0x00000016
MyApp!operator new+0x0000000E
MyApp!LeakyFunc+0x0000001E
MyApp!main+0x0000002C
MyApp!mainCRTStartup+0x000000FC
KERNEL32!BaseProcessStart+0x0000003D
UMDH 输出显示, 没有 21280 (0x5320) 从调用堆栈分配字节总数。 从 20 21280 字节被分配单独分配的 1064 字节 (0x428) (0 x 14)。 标识符是 BackTrace00053 提供调用堆栈。
要生成转储文件的堆分配, 您必须使用 Gflags.exe 实用程序, 也是随 DebuggingToolsforWindows 产品, 来让操作系统知道您想要跟踪分配内核。
假设您要为 Notepad.exe 转储 heap(s) 内容。 必须首先启用堆栈跟踪获取用于对要测试该应用程序。 默认情况下, 不启用此功能。 要启用此功能命令是如下:
gflags notepad.exe + ust - i
命令不启用堆栈跟踪为进程到已经运行, 但使堆栈跟踪对于所有将来执行是 Notepad.exe。 通过 GFLAGS 用户界面 (不带任何参数以获得用户界面运行 Gflags.exe) 或者, 您可设置标志。 使用
- ust 选项对于 gflags 以禁用堆栈跟踪时, 调试已完成。
当您通过 Gflags.exe, 图像标志设并且设置调试符号, 您就可以启动记事本, 使用 UMDH (应用)。 启动程序后, 必须确定进程 ID (PID) 的只启动记事本进程。 对于此命令是如下:
tlist
您可以找到从输出 TLIST 应用程序的 PID。 PID 信息也可获得从任务管理器。 假定对于记事本过程, 您只启动 PID 是 124。 可使用 UMDH 来获取堆转储使用以下命令:
umdh - p 124 个 - f:notepad124.log:
结果 : Notepad124.log 文件中有完整堆的 Notepad 进程转储。 此文件显示所有所做的分配和 callstacks 其中进行分配。
使用 umdh.exe 来比较 UMDH 日志
如果不关心查找内存泄漏, UMDH 日志文件包含对进程, 堆的当前状态宝贵信息时可能会更重要来比较的两个日志输出和了解哪些调用堆栈已看到大增长两转储文件之间。 Umdh.exe 实用程序帮助比较两 UMDH 日志以提供分析之间差。 一旦有两个日志捕获不同间隔, 可再使用以下命令:
UMDH dh1.log dh2.log > cmp12.txt
- 或 -
UMDH -d dh1.log dh2.log > cmp12.txt
- d 命令行选项通知 UMDH 以在十进制代替十六进制显示。 命令的输出比较差异的分配之间两个日志并提供是类似于以下信息:
+ 5320 (f110 - 9df0) = = 5320 3a allocs BackTrace00053 总数增加
对于每 BackTrace, UMDH 日志文件中没有两个日志文件之间进行比较。 本例说明最后一个日志文件 UMDH 命令行中指定有 0xF110 对于相同 BackTrace 调用 (堆栈) 分配字节分配 UMDH 命令行中第一个日志具有 0x9DF0 时字节 UMDH 命令行中第一个日志具有 0x9DF0 时分配字节对于同一 BackTrace (调用堆栈) 分配字节。 " 5320 " 是分配的字节数中差别。 此例, 没有 0x5320 多字节时间之间分配两日志被捕获。 字节来自调用堆栈, 由 " BackTrace00053 "。
下一步是要了解什么是该 backtrace 中。 如果打开二日志文件和搜索
BackTrace00053 , 可能发现内容, 类似于以下内容:
00005320 bytes in 0x14 allocations (@ 0x00000428) by: BackTrace00053
ntdll!RtlDebugAllocateHeap+0x000000FD
ntdll!RtlAllocateHeapSlowly+0x0000005A
ntdll!RtlAllocateHeap+0x00000808
MyApp!_heap_alloc_base+0x00000069
MyApp!_heap_alloc_dbg+0x000001A2
MyApp!_nh_malloc_dbg+0x00000023
MyApp!_nh_malloc+0x00000016
MyApp!operator new+0x0000000E
MyApp!LeakyFunc+0x0000001E
MyApp!main+0x0000002C
MyApp!mainCRTStartup+0x000000FC
KERNEL32!BaseProcessStart+0x0000003D
当您查看调用堆栈, 您可以看到, LeakyFunc 函数分配内存通过 VisualC++ 运行时库运算符新函数。 如果发现, 分配的数增大如采取多转储文件, 您可能断定不被释放内存。
启用堆栈跟踪
UMDH 日志中最重要信息是堆栈跟踪的堆分配。 您可以分析地验证如果进程堆内存泄漏。 默认情况下, 这些堆栈跟踪不获取。 您可启用此功能每个进程或系统级。 使用以下命令以启用堆栈跟踪系统级:
gflags -r + ust
此命令后重新启动计算机。 有关每个进程启用, 命令是如下:
gflags APPNAME + ust - i
其中
APPNAME 是包括扩展名 (例如, Services.exe, Lsass.exe) 可执行的文件名。 命令不启用堆栈跟踪为进程已运行。 因此, 对于进程, 无法重新 (用于示例、 服务、 lsass、 winlogon), 您必须重新启动测试计算机。
使用以下命令以验证哪些设置已设置系统级或特定进程: 系统范围:: 系统范围:
gflags - r
特定进程:
gflags APP - 名称 - i
默认情况下, 大堆栈跟踪深度是 16。 如果要查看深 callstacks, 可以通过运行 GFLAGS 增加此。 单击以选中
系统注册表 , 然后键入新深度
Max. 堆栈跟踪捕获深度 编辑控件中。 单击
应用 , 并重新启动计算机。
要点 : 如果正在使用 Windows NT 4.0 Service Pack 6, 您必须使用 Umdh_nt4.exe, 代替 Umdh.exe, 并且您必须使用
gflags - r 命令来设置系统级堆栈跟踪。 请确保您重新启动计算机。 WindowsNT 版本 4 上每流程基础上无效 Umdh_nt4 堆栈跟踪。 它必须为整个系统设置。
调试符号
最重要步骤以使用 UMDH 之一是以确保您具有好符号文件 (.dbg 或 .pdb 文件) 以获得良好堆栈跟踪。 至少, 您需要 Kernel32.dbg 和 Ntdll.dbg 符号文件。 您可以获得其他调试符号, 您可能需要与您了解多关于哪些组件泄漏内存。 有关如何获取 Microsoft 组件, 调试符号文件请单击文章编号以查看 Microsoft 知识库中相应:
311503
(http://support.microsoft.com/kb/311503/EN-US/) INFO: 使用 Microsoft 符号服务器来获取调试符号文件
有关如何使用 MicrosoftSymbolServer 以及如何获取 Windows 符号程序包, 请访问 Microsoft Web 站点:
http://www.microsoft.com/whdc/devtools/ddk/default.mspx
(http://www.microsoft.com/whdc/devtools/ddk/default.mspx)
当您生成组件与 VisualC++, 值得您没有将程序数据库用于编辑和继续选择用于 C++ 编译器选项。 相反, 选择程序数据库。 将符号路径, 初始化到路径用于 _ NT _ SYMBOL _ PATH 环境变量。 您可以使用 Microsoft 符号服务器来获取用于 Microsoft 组件符号。
311503
(http://support.microsoft.com/kb/311503/EN-US/) INFO: 使用 Microsoft 符号服务器来获取调试符号文件
请按照下列步骤来设置 _ NT _ SYMBOL _ PATH 环境变量:
1. |
在控制面板, 双击 系统 。 |
2. |
单击 高级 选项卡, 然后再单击 环境变量。 |
也可以在命令窗口运行 UMDH 之前设置 _ NT _ SYMBOL _ PATH 环境变量。
注意 : 也包括到 PDB 对组件的应用程序路径。 例如, _ NT _ SYMBOL _ PATH 路径设置为以下:
SRV*c:\symbols*http://msdl.microsoft.com/download/symbols;c:\myapplicationssymbols
第第一部分对此路径指向 MicrosoftSymbolServer 并指出 c:\symbols 文件夹中, 将下载符号使用。 分号后面部分是到 PDB 文件 (符号文件) 专门为泄漏应用程序路径。
调用 UMDH
只需要命令行参数对于 UMDH 是
- p 选项, 指定将从中获取堆转储进程的 PID。 通过使用任务管理器或 Tlist.exe 程序获得 PID。 转日志为类似于以下, 命令将被储到标准输出:
umdh - p PID:
向标准错误, UMDH 还显示各种信息性消息并因此如果您不执行重定向其, 与实际日志混合它。 以收集 UMDH 信息性消息文件, 中使用以下命令:
umdh - p PID 2>umdh.msg:
如果要收集日志是由文件, 中 UMDH 转储, 使用下列命令之一:
umdh - p PID: umdh.log
- 或 -
umdh f:umdh.log PID - - p:
这些命令是等效。
通过 UMDH 获得默认日志包含枚举是堆消费者, 按分配计数排序。 如果还需要所有与其相应堆栈跟踪, 分配块的转储文件用于调试目的,
- d 选项用于:
umdh - p PID - d:
如果使用此命令, 您可能看到以下 UMDH 日志文件中:
对于跟踪 BackTrace00046 分配 005F69A0 005F6150:
这些是用于调用该堆栈分配的内存地址。 如果将调试器附加到进程, 您可转储内存要查看哪些已分配这些地址上的内容。
如果日志包含太多信息, 就可以仅限于那些具有分配大用户计数超过特定阈值。 使用以下命令:
umdh - p PID - t: THRESHOLD:
所有命令行选项 (例如, - p、 - f, - t、 - d) 同时指定按任何顺序。 下面是更难命令行示例:
umdh - p 123: 1000 - f:umdh.log - d - t:
此命令将转储堆对于过程与 PID 123 到 Umdh.log 文件。 它转储只堆栈跟踪, 帐户超过 1000 分配和它的堆块通过每个堆栈跟踪分配地址还转储。
另一个有用 UMDH 选项是
- l 选项。 这导致要尽可能调用堆栈中打印文件和行号。
解释 UMDH 输出
如果您重定向到文件日志 (PID -:
umdh,) 内容是类似于以下, 从运行 Notepad 进程已获得它 - p: f:umdh.log
UMDH: Logtime 2000-06-28 10:54 - Machine=MYMachine - PID=704
*********** Heap 00270000 Information ********************
Flags: 58000062
Number Of Entries: 87
Number Of Tags: <unknown>
Bytes Allocated: 00008DF0
Bytes Committed: 0000A000
Total FreeSpace: 00001210
Number of Virtual Address chunks used: 1
Address Space Used: <unknown>
Entry Overhead: 8
Creator: (Backtrace00007)
ntdll!RtlDebugCreateHeap+0x00000196
ntdll!RtlCreateHeap+0x0000023F
ntdll!LdrpInitializeProcess+0x00000369
ntdll!LdrpInitialize+0x0000028D
ntdll!KiUserApcDispatcher+0x00000007
*********** Heap 00270000 Hogs ********************
000001A0 bytes in 0x4 allocations (@ 0x00000068) by: BackTrace00031
ntdll!RtlDebugAllocateHeap+0x000000FB
ntdll!RtlAllocateHeapSlowly+0x0000005B
ntdll!RtlAllocateHeap+0x00000D81
ntdll!LdrpAllocateDataTableEntry+0x00000039
ntdll!LdrpMapDll+0x000002A4
ntdll!LdrpLoadImportModule+0x0000010D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpInitializeProcess+0x000009DC
ntdll!LdrpInitialize+0x0000028D
ntdll!KiUserApcDispatcher+0x00000007
000001A0 bytes in 0x4 allocations (@ 0x00000068) by: BackTrace00034
ntdll!RtlDebugAllocateHeap+0x000000FB
ntdll!RtlAllocateHeapSlowly+0x0000005B
ntdll!RtlAllocateHeap+0x00000D81
ntdll!LdrpAllocateDataTableEntry+0x00000039
ntdll!LdrpMapDll+0x000002A4
ntdll!LdrpLoadImportModule+0x0000010D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpLoadImportModule+0x0000011D
ntdll!LdrpWalkImportDescriptor+0x0000008B
ntdll!LdrpInitializeProcess+0x000009DC
ntdll!LdrpInitialize+0x0000028D
ntdll!KiUserApcDispatcher+0x00000007
日志包含的进程中每个堆转储。 在本示例, 日志开头 270000 地址处堆。 对于堆, 几全局计数器后日志包含按减少排序顺序的大多数分配负责堆栈跟踪转储。 比较不同时间, 上所使用的内存动态时, 可推断进程中发生什么, 如果任何堆使用它类似于泄漏。
使用 UMDH 时, 您可能会遇到问题
因为未启用堆栈跟踪发生常见错误使用 UMDH 时。 也, 用于 Ntdll.dll 正确符号使 UMDH 无法运行。 对于其他符号文件, UMDH 运行但日志文件包含堆栈跟踪没有函数名称, 但而具有内部模块相对地址。 远程三错误指定错误 PID。 当您尝试为没有启用堆栈跟踪过程运行 UMDH 产生以下错误信息:
C:\>umdh = MyMachine - PID 1140 UMDH = 1140 连接正在完成模块枚举 logtime 06 28 2000 - - - p:: 12:43 - 计算机。 SymGetSymFromName(process, ntdll!RtlpStackTraceDataBase, xxx) 失败, LastError = 126 ntdll UmdhGetAddrFromName 未能找到堆栈跟踪 DB 指针) RtlpStackTraceDataBase ! (。 ntdll.dll 符号不正确 ; 我们必须能够看到非导入符号。
使用以下命令来复查过程, 您正在调查设置:
gflags APPNAME - i
您依赖于系统级堆栈跟踪时使用以下命令:
gflags - r
这些命令显示 flags 设置为应用程序的列表。 注意如果是系统级堆栈跟踪, 功能可能显示为活动但实际上不激活如果您找不重新启动计算机运行
gflags -r + ust 命令, 后。 如果要了解具有启用, 堆栈跟踪每个应用程序可查看以下注册表项下 USTEnabled 项:
HKEY NT\CurrentVersion\Image 文件执行选项
如果 UMDH 具有启用, 堆栈跟踪进程上运行, 但由于您设置标志, 不重新应用日志中可能会收到以下消息:
没有用于此分配保存堆栈跟踪 () 索引 = = 0
如果您运行不要设置正确符号路径或符号不正确, 和运行 UMDH, 日志中可能会收到错误消息。 但是, 只出现错误或误导 callstacks。 若要验证是否有正确符号, 例如启动对进程, NTSD 系统调试器:
ntsd 记事本
然后, 从调试器控制台, 运行
LD 命令以加载符号信息对模块和
LM 命令以模块有加载其符号列表。 如果
LM 命令的输出显示导出符号加载, 符号是不好。 如果您有加载, PDB 符号符号是好。 如果指定错误 PID 可能出现以下错误信息:
OpenProcess 失败, LastError = 0x57
从 VisualBasic 调用 UMDH
最初可能有时用于转储随着时间的日志数量因为泄漏可能不很明显。 例如, 如果怀疑 Active Server Pages (ASP) Web 应用程序, 是泄漏内存, 它可能有助于出 shells, VisualBasic 中写入 UMDH COM 组件。 然后可从您的 ASP 页调用该组件。
下面是一些 VisualBasic 代码调用 UMDH, 创建一个日志文件是基于当前时间:
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Public Function GetProcessID()
GetProcessID = GetCurrentProcessId()
End Function
.
.
.
Dim strTime As String
Dim sProcID As String
sProcID = GetProcessID()
strTime = "MYLOG_" & Format(Now(), "hhmm")
Shell ("C:\UMDH\umdh -p:" & sProcID & " -f:d:\logs\" & strTime & ".txt")
使用 Windows NT 4.0 服务软件包 6a UMDH (SP6a)
Windows NT 4.0 上无效 UMDH 工具随 DebuggingToolsforWindows 产品一起提供。 自解压缩可执行 (Umdhnt4tools.exe) 是随本文并包含以下工具用于与 NT 4.0:
• |
Umdh_nt4.exe 和 Dbghelp.dll 这是 UMDH 实用程序的 Windows NT 4.0 SP 6 版本。 |
• |
Dhcmp.exe 此工具用于比较两 UMDH 转储以确定其中可能内存泄漏 occurrs。 |
可用于从 Microsoft 下载中心下载下列文件:
Download Umdhnt4tools.exe now
(http://download.microsoft.com/download/win2000srv/utility/1.0/nt45/en-us/umdhnt4tools.exe)
August 28, 2002 发布日期:
有关如何下载 Microsoft 支持文件, 请单击下列文章编号以查看 Microsoft 知识库中相应:
119591
(http://support.microsoft.com/kb/119591/EN-US/) 如何从联机服务获取 Microsoft 支持文件
此文件进行病毒扫描 Microsoft。 Microsoft 使用最新病毒检测软件, 投递文件日期上的可用。 文件存储在安全增强型服务器有助于防止未经授权更改对该文件。 文件夹, 置于 Umdh_nt4.exe 和 Dbghelp.dll 并再将它们首先在 PATH 环境变量。 使用 Umdh_nt4.exe 代替 UMDH。
是运行 Windows NT 4.0, 计算机上必须使用 Gflags.exe 来设置系统级堆栈跟踪。 例如:
gflags - r
请确保您重新启动计算机。 WindowsNT 版本 4.0 上逐个进程无效 Umdh_nt4 堆栈跟踪。 设置对整个系统。
与 UMDH UMDH_NT4 是, 它不比较日志文件。 例如, 您无法执行以下操作:
UMDH_NT4 dh1.log dh2.log > cmp12.txt
而必须使用 Dhcmp.exe 工具所附带本文。 类似于以下命令:
DHCMP dh1.log dh2.log > cmp12.txt