调试堆内存
启用用户堆栈跟踪(User Stack Trace
)
- 方法一
- 通过
gflags.exe
为目标进程添加堆栈跟踪标志:
- 通过
1 |
gflags.exe /i YourProgram.exe +ust |
- 方法二:
- 打开
Global Flags
,设置如图示:
- 打开
相关问题
- 在实践过程中,发现勾选
Enable page heap
之后,虽然点击上图的应用确认之后,查看注册表,值为0x02001070
,同时用gflags.exe
在cmd
里面打印的也是0x02001070
,看似正确:- 但实际上,在
windbg
里面,用!gflag
打印出来的值为0x02000000
- 经实践,如果不勾选
Enable page heap
只勾选上图左边四项,注册表值为0x00001070
,且用!gflag
打印出的值也是0x00001070
- 但实际上,在
验证UST
是否生效
- 方法一
Win + R
,输入regedit
打开注册表- 定位到
Image File Execution Options
路径:
若aet_breakpad_test.exe
的键不存在,说明没设置成功 - 检查
GlobalFlag
的值
UST
对应的标志值为0x1000
(即启用用户堆栈跟踪)
1 |
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\aet_breakpad_test.exe |
- 方法二
- 启动
aet_breakpad_test.exe
- 打开
windbg
附加到进程(或者直接用windbg
启动不需要附加) !gflag
:查看全局标志heap -p -a [堆块地址]
:检查堆分配调用栈
若分配堆块时能显示调用栈(如Alloc
函数的堆栈回溯),则UST
生效
- 启动
1 2 3 |
0:000> !gflag Current NtGlobalFlag contents: 0x00001000 ust - Create user mode stack trace database |
!gflag
概述
GlobalFlag
0x02000000
- 启用
hpa
(Heap Placement at Ends of Pages
)
- 启用
0x00001000
- 启用
ust
(User Stack Trace
)
- 启用
0x00000070
- 启用
Heap Tail Checking
- 启用
hpa
- 概述
- 页堆(
Page Heap
) 是Windows
堆管理器提供的调试功能,用于检测堆内存溢出、越界访问等问题 - 其核心原理是通过在堆块后附加 不可访问的栅栏页(
Guard Page
),当程序试图越界写入或读取时,会立即触发访问违规异常(Access Violation
),从而快速定位问题
- 页堆(
- 特性:
- 内存隔离:每个堆块独占一块内存区域,避免相邻堆块干扰
- 溢出检测:堆块尾部设置不可访问的栅栏页,溢出操作直接触发崩溃
- 精准定位:异常发生时,调试器可直接捕获到导致溢出的代码位置
ust
- 概述
- 用户态栈回溯(
User Stack Trace
,UST
)是一种在程序运行中记录函数调用链的技术,用于快速定位内存泄漏、越界访问等问题 - 通过
gflag
工具启用UST
后,堆管理器会在内存分配时记录调用栈信息,便于后续调试器(如WinDbg
)通过!heap -p -a
命令查看完整的函数调用路径
- 用户态栈回溯(
- 场景
- 内存泄漏排查:追踪未释放堆块的分配源头
- 越界访问调试:结合页堆(
Page Heap
)捕获越界操作的调用栈 - 多线程问题分析:识别线程竞争导致的资源未同步释放
htc
- 概述
- 堆尾检查(
Heap Tail Checking
,HTC
) 是Windows
堆管理器提供的一项调试功能,用于检测堆内存的尾部溢出问题
- 堆尾检查(
- 其核心原理是:
- 在堆块的尾部添加特定填充字节(通常为
0xABABABAB
),当程序尝试越界写入堆块尾部时,这些填充字节会被破坏,堆管理器在释放内存或主动检查时触发异常,帮助开发者快速定位溢出位置
- 在堆块的尾部添加特定填充字节(通常为
- 场景
- 越界写入检测:例如数组索引超出范围或字符串拷贝未截断
- 内存破坏分析:如全局变量覆盖堆块尾部数据
- 多线程同步问题:并发操作导致的堆块边界冲突
堆内存
操作系统级内存分配流程
- 当进程启动时,操作系统为其分配虚拟地址空间(
Virtual Address Space
),但此时仅建立虚拟页表,物理内存页并未实际分配 - 首次访问某个虚拟页时触发 页错误(
Page Fault
),操作系统才会通过VirtualAlloc
等API
提交物理内存页,并更新页表映射
new
的调用链路
C++
的new
操作符通过以下链路与操作系统交互:new → operator new → malloc → HeapAlloc → VirtualAlloc
(最终路径)
operator new
的默认实现- 默认情况下,
operator new
调用malloc
(如Microsoft CRT
的实现),而malloc
在 Windows 中底层调用HeapAlloc
- 默认情况下,
HeapAlloc
与VirtualAlloc
的协作HeapAlloc
管理进程的默认堆(Default Heap
),当堆内存不足时,会调用VirtualAlloc
向操作系统申请更大的内存块(通常以1MB
为单位),然后分割为小块供HeapAlloc
管理
new
与物理内存提交的延迟性
- 初次访问触发提交
- 即使
new
成功返回指针,对应的物理内存页可能尚未分配。例如: - 此时,
p[0]
的写入操作会触发页错误,操作系统通过VirtualAlloc
提交物理内存
- 即使
1 2 |
int* p = new int[1024]; // 仅预留虚拟地址空间 p[0] = 42; // 首次写入触发物理页提交 |
性能优化策略
- 预提交内存(
MEM_COMMIT
)- 通过
VirtualAlloc
的MEM_COMMIT
标志强制提交物理内存,避免首次访问的延迟: - 此方法常用于实时性要求高的场景(如高频交易系统)
- 通过
1 |
void* p = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); |
总结(记忆)
- 用
new
在堆上分配的内存,本质上是在进程的虚拟地址空间里面的堆区,去访问虚拟页,虽然可能成功获得了指针,但是对应的物理内存的页框可能并未分配- 用
new
在堆上分配内存时,C++
运行时库(如malloc
)会向操作系统的 堆管理器(如Windows
的HeapAlloc
或Linux
的brk
/mmap
)请求虚拟地址空间 - 操作系统仅在进程首次访问虚拟页时,才会通过 缺页中断(
Page Fault
) 分配物理页框
- 用
UMDH
(User-Mode Dump Heap
)
概述
- Windows 平台下用于检测用户态程序堆内存泄漏的专业工具,属于 Windbg 工具集的一部分
- 它通过记录程序运行过程中堆内存分配的调用栈信息,对比不同时间点的内存快照差异,定位未释放的内存块及其源头代码
工作原理
- 堆内存跟踪机制
UMDH
依赖于Windows
堆管理器的调试功能- 通过启用
gflags
工具的 用户态堆栈追踪数据库(User-Mode Stack Trace Database
),UMDH
可捕获每次内存分配(malloc
/new
)时的调用栈信息
- 快照对比分析
- 通过两次内存快照(
begin.log
和end.log
)的差异,UMDH
识别出在两次快照间新增且未释放的内存分配记录,并关联到具体的代码位置 - 例如,若某段代码在两次快照间分配了
100
次内存但未释放,UMDH
会标记该调用栈为潜在泄漏点
- 通过两次内存快照(
- 符号解析支持
- 需正确配置符号路径(
_NT_SYMBOL_PATH
),确保UMDH
能解析程序及系统库的调试符号(PDB
文件),将内存地址映射到源码行号
- 需正确配置符号路径(
UMDH
使用步骤
- 启用堆栈追踪
- 使用
gflags
为目标程序开启堆栈追踪: - 详细看上面的启用
UST
- 使用
- 配置符号路径
- 设置符号路径,包含程序 PDB 文件和微软符号服务器:
1 |
set _NT_SYMBOL_PATH=C:\YourSymbols;SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols |
- 生成内存快照
1 2 |
umdh -pn:YourProgram.exe -f:begin.log # 初始快照 umdh -pn:YourProgram.exe -f:end.log # 运行一段时间后的快照 |
- 对比快照差异
- 生成差异报告:
- 报告中将列出新增内存分配的调用栈及对应的源码位置
1 |
umdh begin.log end.log -f:diff.log |
关于工具使用
- 需通过命令提示符(
CMD
)或PowerShell
执行相关命令
begin.log
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
// // UMDH: Logtime 2025-03-29 15:16 - Machine=DESKTOP-NRIG174 - PID=23956 // // Debug privilege has been enabled. // OS version 10.0 // // Preparing to dump heap allocations. // Only allocations for which the heap manager collected a stack are dumped. Allocations whithout stack are ignored. // The stack trace for an allocation is dumped as a list of addresses. They will be resolved to function names at compare time. // // Connecting to process 23956 ... // Process 23956 opened handle=260. // // Loaded modules and symbol info: // Base Size ModuleName*ModuleTimeStamp*PdbName*PdbId*PdbAge // 7FF613900000 2F7000 Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.exe*67e67e15*Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.pdb*43e7b8333335468aa4eb0fdb811507b6*3 // 7FFDF53B0000 1F8000 C:\Windows\SYSTEM32\ntdll.dll*ab0dece3*ntdll.pdb*9ff79bba19ebed309623072ea067b20f*1 // 7FFDF41E0000 C2000 C:\Windows\System32\KERNEL32.DLL*5c4539f7*kernel32.pdb*8b1f388f8424b0d61154bc25b96f3792*1 // 7FFDF2EF0000 2FF000 C:\Windows\System32\KERNELBASE.dll*9ce69c7*kernelbase.pdb*6d34aca37853a408fddd2bfe0fc1c19c*1 // // Loaded modules: // Base Size Module // 7FF613900000 2F7000 Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.exe // 7FFDF53B0000 1F8000 C:\Windows\SYSTEM32\ntdll.dll // 7FFDF41E0000 C2000 C:\Windows\System32\KERNEL32.DLL // 7FFDF2EF0000 2FF000 C:\Windows\System32\KERNELBASE.dll // // Process modules enumerated. // Debug library initialized ... 7FFDF53B0000-7FFDF55A7FFF DBGHELP: ntdll - public symbols C:\Symbols\ntdll.pdb\9FF79BBA19EBED309623072EA067B20F1\ntdll.pdb *- - - - - - - - - - Start of data for heap @ 1C40000 - - - - - - - - - - REQUESTED bytes + OVERHEAD at ADDRESS by BackTraceID STACK if not already dumped. *- - - - - - - - - - Heap 1C40000 Hogs - - - - - - - - - - 100 bytes + 50 at 1C408A0 by BackTrace1920C0 7FFDF53DB49D 7FFDF53F31ED 7FFDF53F30AB 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 1D8 bytes + 58 at 1C409F0 by BackTrace192180 7FFDF53DB49D 7FFDF53BDFCC 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 1D8 bytes + 58 at 1C40C20 by BackTrace192240 7FFDF53DB49D 7FFDF5412DAD 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 48 bytes + 58 at 1C40E50 by BackTrace192380 7FFDF53DB49D 7FFDF5412DF7 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 4 bytes + 5C at 1C40EF0 by BackTrace1924C0 7FFDF53DB49D 7FFDF5412EA3 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E // 忽略一部分数据 F0 bytes + 50 at 1C47C70 by BackTrace198E80 7FFDF53DB49D 7FFDF53BF372 7FFDF5412BCE 7FFDF5483B18 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E *- - - - - - - - - - End of data for heap @ 1C40000 - - - - - - - - - - *- - - - - - - - - - Start of data for heap @ 10000 - - - - - - - - - - REQUESTED bytes + OVERHEAD at ADDRESS by BackTraceID STACK if not already dumped. *- - - - - - - - - - Heap 10000 Hogs - - - - - - - - - - *- - - - - - - - - - End of data for heap @ 10000 - - - - - - - - - - |
end.log
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
// // UMDH: Logtime 2025-03-29 15:17 - Machine=DESKTOP-NRIG174 - PID=23956 // // Debug privilege has been enabled. // OS version 10.0 // // Preparing to dump heap allocations. // Only allocations for which the heap manager collected a stack are dumped. Allocations whithout stack are ignored. // The stack trace for an allocation is dumped as a list of addresses. They will be resolved to function names at compare time. // // Connecting to process 23956 ... // Process 23956 opened handle=260. // // Loaded modules and symbol info: // Base Size ModuleName*ModuleTimeStamp*PdbName*PdbId*PdbAge // 7FF613900000 2F7000 Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.exe*67e67e15*Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.pdb*43e7b8333335468aa4eb0fdb811507b6*3 // 7FFDF53B0000 1F8000 C:\Windows\SYSTEM32\ntdll.dll*ab0dece3*ntdll.pdb*9ff79bba19ebed309623072ea067b20f*1 // 7FFDF41E0000 C2000 C:\Windows\System32\KERNEL32.DLL*5c4539f7*kernel32.pdb*8b1f388f8424b0d61154bc25b96f3792*1 // 7FFDF2EF0000 2FF000 C:\Windows\System32\KERNELBASE.dll*9ce69c7*kernelbase.pdb*6d34aca37853a408fddd2bfe0fc1c19c*1 // 7FFDE4F30000 1E4000 C:\Windows\SYSTEM32\dbghelp.dll*9a866525*dbghelp.pdb*961ac3bbb51c2d60a763d5fd89f820a7*1 // 7FFDF2DF0000 100000 C:\Windows\System32\ucrtbase.dll*81cf5d89*ucrtbase.pdb*5c4f64bf99f5fdec7295cab05a30010d*1 // 7FFDD56B0000 34000 C:\Windows\SYSTEM32\dbgcore.DLL*a4254c09*dbgcore.pdb*6a072ad6b41ec0723dc8d38fdb3bae92*1 // 7FFDF4C80000 123000 C:\Windows\System32\rpcrt4.dll*782a457b*rpcrt4.pdb*152bd4e8ac31a21fce1cb8eec5221fd3*1 // 7FFDF2A80000 82000 C:\Windows\System32\bcryptPrimitives.dll*c54120ed*bcryptprimitives.pdb*9025181284470d297546974ad8e6867a*1 // // Loaded modules: // Base Size Module // 7FF613900000 2F7000 Q:\google_code\breakpad\src\src\client\windows\x64\Debug\aet_breakpad_test.exe // 7FFDF53B0000 1F8000 C:\Windows\SYSTEM32\ntdll.dll // 7FFDF41E0000 C2000 C:\Windows\System32\KERNEL32.DLL // 7FFDF2EF0000 2FF000 C:\Windows\System32\KERNELBASE.dll // 7FFDE4F30000 1E4000 C:\Windows\SYSTEM32\dbghelp.dll // 7FFDF2DF0000 100000 C:\Windows\System32\ucrtbase.dll // 7FFDD56B0000 34000 C:\Windows\SYSTEM32\dbgcore.DLL // 7FFDF4C80000 123000 C:\Windows\System32\rpcrt4.dll // 7FFDF2A80000 82000 C:\Windows\System32\bcryptPrimitives.dll // // Process modules enumerated. // Debug library initialized ... 7FFDF53B0000-7FFDF55A7FFF DBGHELP: ntdll - public symbols C:\Symbols\ntdll.pdb\9FF79BBA19EBED309623072EA067B20F1\ntdll.pdb *- - - - - - - - - - Start of data for heap @ 1C40000 - - - - - - - - - - REQUESTED bytes + OVERHEAD at ADDRESS by BackTraceID STACK if not already dumped. *- - - - - - - - - - Heap 1C40000 Hogs - - - - - - - - - - 100 bytes + 50 at 1C408A0 by BackTrace1920C0 7FFDF53DB49D 7FFDF53F31ED 7FFDF53F30AB 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 1D8 bytes + 58 at 1C409F0 by BackTrace192180 7FFDF53DB49D 7FFDF53BDFCC 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 1D8 bytes + 58 at 1C40C20 by BackTrace192240 7FFDF53DB49D 7FFDF5412DAD 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 48 bytes + 58 at 1C40E50 by BackTrace192380 7FFDF53DB49D 7FFDF5412DF7 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 4 bytes + 5C at 1C40EF0 by BackTrace1924C0 7FFDF53DB49D 7FFDF5412EA3 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 10 bytes + 50 at 1C40F50 by BackTrace192600 7FFDF53DB49D 7FFDF5412EE1 7FFDF53C388C 7FFDF53C2634 7FFDF53C22CD 7FFDF53C09A3 7FFDF53BE02C 7FFDF542E41E 7FFDF53F38C0 7FFDF53F2F43 7FFDF53F30E5 7FFDF53F2EA0 7FFDF5482878 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 100 bytes + 50 at 1C40FB0 by BackTrace1929C0 7FFDF53DB49D 7FFDF53F31ED 7FFDF53F30AB 7FFDF53F2EA0 7FFDF5482895 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 100 bytes + 50 at 1C41100 by BackTrace192A80 7FFDF53DB49D 7FFDF53F31ED 7FFDF53F30AB 7FFDF53F2EA0 7FFDF54828B2 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E 2064 bytes + 5C at 1C41250 by BackTrace192B40 7FFDF53DB49D 7FFDF53FB001 7FFDF54828CC 7FFDF5425DEB 7FFDF5425C73 7FFDF5425C1E // 忽略一部分数据 18C bytes + 54 at 1C66540 by BackTrace1F2520 7FFDF53DB49D 7FF613A9BD35 7FF613A9BFCD 7FF613A9B2CC 7FF613B17FE0 7FF613B1768D 7FF613B178E9 7FF613B1A2B1 7FF613AF8A0A 7FF613AF827E 7FF613AF8056 7FF613AF8117 7FF613AF8AD3 7FF6139F90EC 7FF6139E050C 7FF6139E6D85 7FF6139E1704 7FF6139E786A 7FF6139EA54C 7FF6139ED016 7FF613A1B89A 7FF6139E485C 7FF613A72619 7FF613A724C2 7FF613A7237E 7FF613A726AE 7FFDF41F7374 7FFDF53FCC91 *- - - - - - - - - - End of data for heap @ 1C40000 - - - - - - - - - - *- - - - - - - - - - Start of data for heap @ 10000 - - - - - - - - - - REQUESTED bytes + OVERHEAD at ADDRESS by BackTraceID STACK if not already dumped. *- - - - - - - - - - Heap 10000 Hogs - - - - - - - - - - *- - - - - - - - - - End of data for heap @ 10000 - - - - - - - - - - |
diff.log
- 部分内容如下
+5c
:(0x5C = 92
字节)表示两次快照间新增了92
字节未释放的内存+1
:表示在此期间有1
次内存分配未被释放- 这些数据表明在两次快照期间,存在 内存泄漏
- 明确指向代码
main
函数第52
行,即未释放的new
操作位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
+ 5c ( 5c - 0) 1 allocs BackTrace1F3960 + 1 ( 1 - 0) BackTrace1F3960 allocations ntdll!RtlpAllocateHeapInternal+A7D aet_breakpad_test!heap_alloc_dbg_internal+205 (minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp, 359) aet_breakpad_test!heap_alloc_dbg+4D (minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp, 450) aet_breakpad_test!_malloc_dbg+2F (minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp, 496) aet_breakpad_test!malloc+1E (minkernel\crts\ucrt\src\appcrt\heap\malloc.cpp, 27) aet_breakpad_test!operator new+13 (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp, 36) aet_breakpad_test!operator new[]+13 (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\heap\new_array.cpp, 30) aet_breakpad_test!main+94 (Q:\google_code\breakpad\src\src\client\windows\aet_test\aet_breakpad_test\aet_breakpad_test.cpp, 52) aet_breakpad_test!invoke_main+39 (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, 79) aet_breakpad_test!__scrt_common_main_seh+132 (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, 288) aet_breakpad_test!__scrt_common_main+E (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, 331) aet_breakpad_test!mainCRTStartup+E (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp, 17) KERNEL32!BaseThreadInitThunk+14 ntdll!RtlUserThreadStart+21 |
!heap
!heap -s
-
WinDbg
中用于 显示堆的摘要信息 的命令,主要用于快速查看进程内所有堆的全局状态- 堆内存统计:显示每个堆的保留(
Reserved
)和提交(Committed
)内存大小 - 堆块分布分析:展示空闲块(
Free Blocks
)与已分配块(Busy Blocks
)的数量及大小分布 - 堆管理器状态:验证堆元数据完整性(需配合
-v
参数),检测潜在内存损坏
- 堆内存统计:显示每个堆的保留(
-
示例如下:
NtGlobalFlag
:当前启用的全局调试标志stack back traces
:表示堆分配时会记录调用栈(需配合ust
标志),用于追踪内存泄漏或越界操作LFH Key
:低碎片堆(Low Fragmentation Heap
,LFH
)的加密密钥,用于验证堆操作的合法性,防止堆溢出攻击Termination on corruption
:检测到堆内存损坏时自动终止进程,防止进一步破坏系统稳定性Heap
:堆的起始地址(虚拟内存地址)
0000000001cc0000
表示第一个堆的基址Flags
:堆的属性标志(十六进制值),反映堆的配置特性
08000002
表示堆支持动态扩展、启用LFH
Reserv (k)
:堆保留的虚拟内存总量(单位:KB
),即操作系统为此堆预留的地址空间
1800
表示保留6,144 KB
(1.5GB
)Commit (k)
:堆已提交的物理内存量(单位:KB
),即实际被占用的内存
164
表示已提交164 KB
Virt (k)
:堆当前可用的虚拟内存量(单位:KB
)
1020
表示虚拟内存剩余1,020 KB
Free (k)
:堆中空闲内存块的总大小(单位:KB
)
5
表示当前堆有5 KB
空闲内存List length
:空闲链表(Free List
)中的内存块数量
11
表示空闲链表包含11
个内存块UCR
:未提交范围(Uncommitted Ranges
)数量,表示堆扩展时未实际分配物理内存的区域数量
1
表示有1
个未提交区域Virt blocks
:连续内存块的数量,反映堆的碎片化程度
0
表示无连续大块内存Lock cont.
:堆锁的争用次数,高频率表示多线程竞争激烈
0
表示当前无锁争用Fast Heap
:是否启用低碎片堆(Low Fragmentation Heap
)
LFH
表示此堆已启用LFH
优化分配效率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
0:000> !heap -s ************************************************************************************************************************ NT HEAP STATS BELOW ************************************************************************************************************************ NtGlobalFlag enables following debugging aids for new heaps: stack back traces LFH Key : 0xb6690a7296aedcf9 Termination on corruption : ENABLED Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast (k) (k) (k) (k) length blocks cont. heap ------------------------------------------------------------------------------------- 0000000001cc0000 08000002 1800 164 1020 5 11 1 0 0 LFH 0000000000010000 08008000 64 4 64 2 1 1 0 0 ------------------------------------------------------------------------------------- |
- 实际应用:
- 定位内存泄漏:
若某个堆的Commit
持续增长而Free
减少,可能存在未释放的内存块 - 分析碎片化:
Virt blocks
为0
且List length
较高时,说明堆内存碎片化严重,可能影响性能 LFH
优化验证
若Flags
包含LFH
,表示系统已对此堆启用低碎片分配策略,适合高频小内存分配场景
- 定位内存泄漏:
!heap -h <HeapAddress>
-
用于 显示指定堆的详细信息,包括堆的元数据结构、内存段分布、空闲列表状态等
- 堆元数据分析:展示堆的全局管理结构(如
_HEAP
结构体),验证堆完整性 - 内存段分布:列出堆的虚拟地址范围(起始地址、结束地址)及提交内存状态
- 空闲块管理:显示空闲链表的分配情况,用于分析堆碎片化问题
- 显示堆的分配粒度(
Granularity
)、段(Segment
)分布、空闲列表(FreeList
)等底层属性
- 堆元数据分析:展示堆的全局管理结构(如
-
示例如下:
08000002
:
0x08000000
:启用低碎片堆(LFH
)优化
0x00000002
:支持动态扩展(HEAP_GROWABLE
)Segment at 0000000001cc0000 to 0000000001dbf000
0000000001cc0000
到0000000001dbf000
,总保留空间为00027000 bytes
(159 KB
)
00027000 bytes
中已提交00000162 bytes
(354 bytes
)供实际使用Granularity
(粒度):16 bytes
,堆分配的最小单位Segment Reserve/Commit
:保留00100000
(1 MB
),每次提交00002000
(8 KB
)Total Free Size
:00000162 bytes
(354
字节),空闲内存较少,碎片化程度低FreeList[00]
:空闲链表头位于0000000001cc0150
,包含11 blocks
空闲块
包含11
个空闲块,但总空闲内存仅占堆的0.2%
,说明内存利用率较高
-
示例如下,堆条目解析:
address
:内存块起始地址(如0000000001cc0000
)psize
:前一块的大小(用于内存合并优化)
0
(起始块无前驱)size
:当前块的总大小(单位:字节)flags
:状态标识(如[101]
表示占用块头部信息)state
:busy
(已分配)或free
(空闲)requested size
:用户实际请求的内存大小(可能小于size
因对齐)Internal
:表示该块为堆内部管理结构(如元数据),非用户数据Uncommitted
:未提交的虚拟内存区域(如000d8000
未提交空间)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
0:000> !heap -h 0000000001cc0000 Index Address Name Debugging options enabled 1: 01cc0000 Segment at 0000000001cc0000 to 0000000001dbf000 (00027000 bytes committed) Flags: 08000002 ForceFlags: 00000000 Granularity: 16 bytes Segment Reserve: 00100000 Segment Commit: 00002000 DeCommit Block Thres: 00000400 DeCommit Total Thres: 00001000 Total Free Size: 00000162 Max. Allocation Size: 00007ffffffdefff Lock Variable at: 0000000001cc02c0 Next TagIndex: 0000 Maximum TagIndex: 0000 Tag Entries: 00000000 PsuedoTag Entries: 00000000 Virtual Alloc List: 01cc0110 Uncommitted ranges: 01cc00f0 FreeList[ 00 ] at 0000000001cc0150: 0000000001ce5b80 . 0000000001cd6e80 (11 blocks) Heap entries for Segment00 in Heap 0000000001cc0000 address: psize . size flags state (requested size) 0000000001cc0000: 00000 . 00740 [101] - busy (73f) 0000000001cc0740: 00740 . 00060 [101] - busy (30) 0000000001cc07a0: 00060 . 00080 [101] - busy (53) 0000000001cc0820: 00080 . 00030 [100] 0000000001cc0850: 00030 . 00130 [101] - busy (100) 0000000001cc0980: 00130 . 00200 [101] - busy (1d8) 0000000001cc0b80: 00200 . 00200 [101] - busy (1d8) 0000000001cc0d80: 00200 . 00070 [101] - busy (48) 0000000001cc0df0: 00070 . 00030 [101] - busy (4) 0000000001cc0e20: 00030 . 00040 [101] - busy (10) 0000000001cc0e60: 00040 . 00130 [101] - busy (100) 0000000001cc0f90: 00130 . 00130 [101] - busy (100) 0000000001cc10c0: 00130 . 01e90 [101] - busy (1e5a) 0000000001cc2f50: 01e90 . 00870 [101] - busy (844) 0000000001cc37c0: 00870 . 00070 [101] - busy (3c) 0000000001cc3830: 00070 . 00060 [101] - busy (30) 0000000001cc3890: 00060 . 00090 [101] - busy (62) 0000000001cc3920: 00090 . 00150 [101] - busy (120) 0000000001cc3a70: 00150 . 00080 [101] - busy (50) 0000000001cc3af0: 00080 . 00150 [101] - busy (120) 0000000001cc3c40: 00150 . 00080 [101] - busy (50) 0000000001cc3cc0: 00080 . 00260 [101] - busy (238) 0000000001cc3f20: 00260 . 00080 [101] - busy (50) 0000000001cc3fa0: 00080 . 00060 [101] - busy (30) 0000000001cc4000: 00060 . 00030 [100] 0000000001cc4030: 00030 . 00150 [101] - busy (120) 0000000001cc4180: 00150 . 00080 [101] - busy (50) 0000000001cc4200: 00080 . 00070 [101] - busy (42) 0000000001cc4270: 00070 . 00200 [101] - busy (1d8) 0000000001cc4470: 00200 . 00130 [101] - busy (f0) 0000000001cc45a0: 00130 . 00050 [101] - busy (20) 0000000001cc45f0: 00050 . 00040 [101] - busy (10) 0000000001cc4630: 00040 . 00060 [101] - busy (30) 0000000001cc4690: 00060 . 00070 [101] - busy (30) 0000000001cc4700: 00070 . 00150 [101] - busy (120) 0000000001cc4850: 00150 . 00080 [101] - busy (50) 0000000001cc48d0: 00080 . 00070 [101] - busy (46) 0000000001cc4940: 00070 . 00030 [101] - busy (4) 0000000001cc4970: 00030 . 00020 [100] 0000000001cc4990: 00020 . 00070 [101] - busy (48) 0000000001cc4a00: 00070 . 00040 [101] - busy (18) 0000000001cc4a40: 00040 . 00080 [101] - busy (50) 0000000001cc4ac0: 00080 . 00070 [101] - busy (42) 0000000001cc4b30: 00070 . 00080 [101] - busy (4f) 0000000001cc4bb0: 00080 . 00430 [101] - busy (400) 0000000001cc4fe0: 00430 . 00430 [101] - busy (400) 0000000001cc5410: 00430 . 00080 [101] - busy (50) 0000000001cc5490: 00080 . 00090 [101] - busy (68) 0000000001cc5520: 00090 . 00060 [101] - busy (30) 0000000001cc5580: 00060 . 00130 [101] - busy (100) 0000000001cc56b0: 00130 . 00060 [101] - busy (30) 0000000001cc5710: 00060 . 00060 [101] - busy (30) 0000000001cc5770: 00060 . 00160 [101] - busy (130) 0000000001cc58d0: 00160 . 00060 [101] - busy (30) 0000000001cc5930: 00060 . 00070 [101] - busy (40) 0000000001cc59a0: 00070 . 00070 [101] - busy (40) 0000000001cc5a10: 00070 . 00060 [101] - busy (38) 0000000001cc5a70: 00060 . 00050 [101] - busy (20) 0000000001cc5ac0: 00050 . 00090 [101] - busy (60) 0000000001cc5b50: 00090 . 00190 [101] - busy (168) 0000000001cc5ce0: 00190 . 00200 [101] - busy (1d8) 0000000001cc5ee0: 00200 . 000d0 [101] - busy (a8) 0000000001cc5fb0: 000d0 . 000c0 [101] - busy (98) 0000000001cc6070: 000c0 . 00060 [101] - busy (30) 0000000001cc60d0: 00060 . 00120 [101] - busy (f0) 0000000001cc61f0: 00120 . 000d0 [101] - busy (a8) 0000000001cc62c0: 000d0 . 000c0 [101] - busy (98) 0000000001cc6380: 000c0 . 000d0 [101] - busy (a8) 0000000001cc6450: 000d0 . 000c0 [101] - busy (98) 0000000001cc6510: 000c0 . 00070 [101] - busy (40) 0000000001cc6580: 00070 . 00070 [101] - busy (40) 0000000001cc65f0: 00070 . 00190 [101] - busy (168) 0000000001cc6780: 00190 . 00060 [101] - busy (38) 0000000001cc67e0: 00060 . 00060 [101] - busy (38) 0000000001cc6840: 00060 . 00070 [101] - busy (48) 0000000001cc68b0: 00070 . 00020 [100] 0000000001cc68d0: 00020 . 003f0 [101] - busy (3c0) 0000000001cc6cc0: 003f0 . 00130 [101] - busy (100) 0000000001cc6df0: 00130 . 00130 [101] - busy (100) 0000000001cc6f20: 00130 . 00130 [101] - busy (100) 0000000001cc7050: 00130 . 00190 [101] - busy (168) 0000000001cc71e0: 00190 . 00130 [101] - busy (108) 0000000001cc7310: 00130 . 000b0 [101] - busy (88) 0000000001cc73c0: 000b0 . 00060 [101] - busy (30) 0000000001cc7420: 00060 . 00060 [101] - busy (30) 0000000001cc7480: 00060 . 00060 [101] - busy (30) 0000000001cc74e0: 00060 . 00060 [101] - busy (30) 0000000001cc7540: 00060 . 00060 [101] - busy (30) 0000000001cc75a0: 00060 . 00810 [101] - busy (80f) Internal 0000000001cc7db0: 00810 . 01cb0 [101] - busy (1caf) Internal 0000000001cc9a60: 01cb0 . 00830 [101] - busy (800) Internal 0000000001cca290: 00830 . 00420 [101] - busy (3f0) Internal 0000000001cca6b0: 00420 . 00430 [101] - busy (3fc) 0000000001ccaae0: 00430 . 01260 [101] - busy (1234) 0000000001ccbd40: 01260 . 01030 [101] - busy (1000) Internal 0000000001cccd70: 01030 . 01030 [101] - busy (1000) Internal 0000000001ccdda0: 01030 . 00290 [101] - busy (25c) 0000000001cce030: 00290 . 00160 [101] - busy (134) 0000000001cce190: 00160 . 000c0 [101] - busy (93) 0000000001cce250: 000c0 . 01030 [101] - busy (1000) Internal 0000000001ccf280: 01030 . 00730 [101] - busy (706) 0000000001ccf9b0: 00730 . 00070 [101] - busy (47) 0000000001ccfa20: 00070 . 00090 [101] - busy (5d) 0000000001ccfab0: 00090 . 00100 [101] - busy (d3) 0000000001ccfbb0: 00100 . 00070 [101] - busy (48) 0000000001ccfc20: 00070 . 00070 [101] - busy (43) 0000000001ccfc90: 00070 . 00090 [101] - busy (5b) 0000000001ccfd20: 00090 . 00090 [101] - busy (5a) 0000000001ccfdb0: 00090 . 00090 [101] - busy (5e) 0000000001ccfe40: 00090 . 000b0 [101] - busy (86) 0000000001ccfef0: 000b0 . 000b0 [101] - busy (7b) 0000000001ccffa0: 000b0 . 000a0 [101] - busy (6f) 0000000001cd0040: 000a0 . 000a0 [101] - busy (74) 0000000001cd00e0: 000a0 . 000b0 [101] - busy (88) 0000000001cd0190: 000b0 . 00050 [100] 0000000001cd01e0: 00050 . 00050 [101] - busy (20) 0000000001cd0230: 00050 . 00050 [101] - busy (1f) 0000000001cd0280: 00050 . 00020 [100] 0000000001cd02a0: 00020 . 00260 [101] - busy (234) 0000000001cd0500: 00260 . 001e0 [101] - busy (1ac) 0000000001cd06e0: 001e0 . 00150 [101] - busy (120) 0000000001cd0830: 00150 . 00150 [101] - busy (120) 0000000001cd0980: 00150 . 000b0 [101] - busy (88) 0000000001cd0a30: 000b0 . 003c0 [101] - busy (390) 0000000001cd0df0: 003c0 . 00250 [101] - busy (228) 0000000001cd1040: 00250 . 00040 [101] - busy (d) 0000000001cd1080: 00040 . 00230 [101] - busy (1fc) 0000000001cd12b0: 00230 . 00090 [101] - busy (66) 0000000001cd1340: 00090 . 00090 [101] - busy (5b) 0000000001cd13d0: 00090 . 00090 [101] - busy (5c) 0000000001cd1460: 00090 . 00090 [101] - busy (60) 0000000001cd14f0: 00090 . 00090 [101] - busy (60) 0000000001cd1580: 00090 . 00090 [101] - busy (65) 0000000001cd1610: 00090 . 000a0 [101] - busy (70) 0000000001cd16b0: 000a0 . 00090 [101] - busy (65) 0000000001cd1740: 00090 . 00080 [101] - busy (51) 0000000001cd17c0: 00080 . 00080 [101] - busy (58) 0000000001cd1840: 00080 . 00080 [101] - busy (50) 0000000001cd18c0: 00080 . 00090 [101] - busy (66) 0000000001cd1950: 00090 . 00090 [101] - busy (65) 0000000001cd19e0: 00090 . 00090 [101] - busy (5c) 0000000001cd1a70: 00090 . 00070 [101] - busy (41) 0000000001cd1ae0: 00070 . 00080 [101] - busy (4a) 0000000001cd1b60: 00080 . 00090 [101] - busy (5e) 0000000001cd1bf0: 00090 . 00080 [101] - busy (52) 0000000001cd1c70: 00080 . 00070 [101] - busy (46) 0000000001cd1ce0: 00070 . 00090 [101] - busy (5d) 0000000001cd1d70: 00090 . 00070 [101] - busy (42) 0000000001cd1de0: 00070 . 000a0 [101] - busy (72) 0000000001cd1e80: 000a0 . 00090 [101] - busy (67) 0000000001cd1f10: 00090 . 000b0 [101] - busy (7c) 0000000001cd1fc0: 000b0 . 00830 [101] - busy (800) Internal 0000000001cd27f0: 00830 . 01030 [101] - busy (1000) Internal 0000000001cd3820: 01030 . 01060 [101] - busy (1034) 0000000001cd4880: 01060 . 004a0 [101] - busy (470) 0000000001cd4d20: 004a0 . 000a0 [101] - busy (6c) 0000000001cd4dc0: 000a0 . 001c0 [101] - busy (18c) 0000000001cd4f80: 001c0 . 00040 [101] - busy (18) 0000000001cd4fc0: 00040 . 007c0 [101] - busy (790) 0000000001cd5780: 007c0 . 00250 [101] - busy (228) 0000000001cd59d0: 00250 . 001f0 [101] - busy (1c8) 0000000001cd5bc0: 001f0 . 00050 [101] - busy (27) 0000000001cd5c10: 00050 . 00050 [101] - busy (28) 0000000001cd5c60: 00050 . 00050 [101] - busy (1d) 0000000001cd5cb0: 00050 . 00050 [101] - busy (24) 0000000001cd5d00: 00050 . 00050 [101] - busy (1c) 0000000001cd5d50: 00050 . 00020 [100] 0000000001cd5d70: 00020 . 00f00 [101] - busy (ed6) 0000000001cd6c70: 00f00 . 00150 [101] - busy (120) 0000000001cd6dc0: 00150 . 000b0 [101] - busy (84) 0000000001cd6e70: 000b0 . 00020 [100] 0000000001cd6e90: 00020 . 00130 [101] - busy (100) 0000000001cd6fc0: 00130 . 01230 [101] - busy (1200) 0000000001cd81f0: 01230 . 01030 [101] - busy (1000) Internal 0000000001cd9220: 01030 . 01030 [101] - busy (1000) Internal 0000000001cda250: 01030 . 00830 [101] - busy (800) Internal 0000000001cdaa80: 00830 . 01030 [101] - busy (1000) Internal 0000000001cdbab0: 01030 . 01030 [101] - busy (1000) 0000000001cdcae0: 01030 . 000d0 [101] - busy (a4) 0000000001cdcbb0: 000d0 . 00060 [100] 0000000001cdcc10: 00060 . 00420 [101] - busy (3f0) Internal 0000000001cdd030: 00420 . 00020 [100] 0000000001cdd050: 00020 . 00050 [101] - busy (28) 0000000001cdd0a0: 00050 . 00040 [101] - busy (16) 0000000001cdd0e0: 00040 . 00050 [101] - busy (1e) 0000000001cdd130: 00050 . 00040 [101] - busy (12) 0000000001cdd170: 00040 . 00040 [101] - busy (18) 0000000001cdd1b0: 00040 . 00050 [101] - busy (21) 0000000001cdd200: 00050 . 00040 [101] - busy (e) 0000000001cdd240: 00040 . 00700 [101] - busy (6d2) 0000000001cdd940: 00700 . 00050 [101] - busy (1d) 0000000001cdd990: 00050 . 00040 [101] - busy (13) 0000000001cdd9d0: 00040 . 00040 [101] - busy (18) 0000000001cdda10: 00040 . 00050 [101] - busy (1b) 0000000001cdda60: 00050 . 00050 [101] - busy (1e) 0000000001cddab0: 00050 . 00050 [101] - busy (1e) 0000000001cddb00: 00050 . 000d0 [101] - busy (9f) 0000000001cddbd0: 000d0 . 00040 [101] - busy (17) 0000000001cddc10: 00040 . 00040 [101] - busy (14) 0000000001cddc50: 00040 . 00050 [101] - busy (1e) 0000000001cddca0: 00050 . 00040 [101] - busy (18) 0000000001cddce0: 00040 . 00040 [101] - busy (f) 0000000001cddd20: 00040 . 00040 [101] - busy (16) 0000000001cddd60: 00040 . 00040 [101] - busy (10) 0000000001cddda0: 00040 . 00830 [101] - busy (800) Internal 0000000001cde5d0: 00830 . 01030 [101] - busy (1000) Internal 0000000001cdf600: 01030 . 01030 [101] - busy (1000) Internal 0000000001ce0630: 01030 . 01030 [101] - busy (1000) Internal 0000000001ce1660: 01030 . 01030 [101] - busy (1000) Internal 0000000001ce2690: 01030 . 01030 [101] - busy (1000) Internal 0000000001ce36c0: 01030 . 01030 [101] - busy (1000) Internal 0000000001ce46f0: 01030 . 00430 [101] - busy (400) 0000000001ce4b20: 00430 . 007c0 [101] - busy (790) 0000000001ce52e0: 007c0 . 00150 [101] - busy (120) 0000000001ce5430: 00150 . 00150 [101] - busy (120) 0000000001ce5580: 00150 . 00130 [101] - busy (100) 0000000001ce56b0: 00130 . 00130 [101] - busy (100) 0000000001ce57e0: 00130 . 00230 [101] - busy (200) 0000000001ce5a10: 00230 . 00160 [101] - busy (130) 0000000001ce5b70: 00160 . 01450 [100] 0000000001ce6fc0: 01450 . 00040 [111] - busy (3d) 0000000001ce7000: 000d8000 - uncommitted bytes. |
!heap -p -a <HeapAddress>
-
主要用于 分析启用了页堆(
Page Heap
)的堆块分配信息,具体功能包括:- 堆块元数据解析:显示堆块的分配大小、用户指针地址、填充模式(如
0xABABABAB
)等 - 调用栈回溯:若启用了用户模式堆栈跟踪(
+ust
),可显示分配时的调用栈(需符号文件支持) - 内存破坏检测:通过填充字节验证(如被篡改的填充模式)检测堆溢出或非法访问问题
- 显示分配块的元数据(如
DPH_HEAP_BLOCK
结构)、调用栈跟踪(需启用ust
标志),直接定位分配代码位置
- 堆块元数据解析:显示堆块的分配大小、用户指针地址、填充模式(如
-
示例分析如下:
_HEAP @ 1cc0000
表示该内存块属于地址为1cc0000
的堆HEAP_ENTRY
: 元数据UserPtr
:用户数据起始地址0000000001cc0770
(0000000001cc0740 + 0x30
,因堆头占用0x30
字节)UserSize
:用户请求的分配大小为0x30
字节(48
字节)Flags
:[00]
表示普通堆块(非低碎片堆LFH
或页堆)State
:busy
表示该内存块已被分配且未释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
0:000> !heap -p -a 0000000001cc0740 address 0000000001cc0740 found in _HEAP @ 1cc0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0000000001cc0740 0006 0000 [00] 0000000001cc0770 00030 - (busy) 7ffe58c9b49d ntdll!RtlpAllocateHeapInternal+0x0000000000000a7d 7ffe58c81882 ntdll!RtlpAddDebugInfoToCriticalSection+0x0000000000000042 7ffe58c90cf8 ntdll!RtlInitializeCriticalSectionEx+0x0000000000000138 7ffe567e8f7d KERNELBASE!InitializeCriticalSectionEx+0x000000000000000d 7ff7a8236156 aet_breakpad_test!__acrt_InitializeCriticalSectionEx+0x0000000000000026 7ff7a81f7e94 aet_breakpad_test!__acrt_initialize_locks+0x0000000000000044 7ff7a825b69b aet_breakpad_test!__acrt_execute_initializers+0x000000000000006b 7ff7a821d9e0 aet_breakpad_test!__acrt_initialize+0x0000000000000020 7ff7a8173031 aet_breakpad_test!__scrt_initialize_crt+0x0000000000000031 7ff7a817239e aet_breakpad_test!__scrt_common_main_seh+0x000000000000000e 7ff7a817237e aet_breakpad_test!__scrt_common_main+0x000000000000000e 7ff7a81726ae aet_breakpad_test!mainCRTStartup+0x000000000000000e 7ffe589a7374 KERNEL32!BaseThreadInitThunk+0x0000000000000014 7ffe58cbcc91 ntdll!RtlUserThreadStart+0x0000000000000021 |
!heap -stat -h <HeapAddress>
-
用于 统计指定堆的内存分配情况,按块大小分组显示各分组的分配数量及占用的总内存量
- 内存分布分析:按分配大小(如
16
字节、32
字节等)分类统计,快速识别高频分配或潜在泄漏点 - 堆使用趋势判断:通过不同大小块的数量和占比,评估堆的碎片化程度
- 异常分配检测:发现异常大小的内存块(如远大于预期的分配)
- 按分配块大小分组统计,显示
AllocSize
、#Blocks
和TotalMem
,用于识别高频分配的内存大小
- 内存分布分析:按分配大小(如
-
示例分析如下:
size
:内存块大小(十六进制,单位:字节)#blocks
:该大小的内存块数量total
:该大小内存块占用的总字节数(十六进制)(%)
:占堆中“繁忙内存”(已分配内存)的比例
-
示例分析如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
0:000> !heap -stat -h 1cc0000 heap @ 0000000001cc0000 group-by: TOTSIZE max-display: 20 size #blocks total ( %) (percent of total busy bytes) 30 cb - 2610 (10.92) 1e5a 1 - 1e5a (8.71) 10 12f - 12f0 (5.43) 1234 1 - 1234 (5.22) 1200 1 - 1200 (5.17) 1034 1 - 1034 (4.65) 1000 1 - 1000 (4.59) 790 2 - f20 (4.34) ed6 1 - ed6 (4.26) 400 3 - c00 (3.44) 120 9 - a20 (2.91) 100 a - a00 (2.87) 844 1 - 844 (2.37) 1d8 4 - 760 (2.12) 706 1 - 706 (2.02) 6d2 1 - 6d2 (1.96) 50 f - 4b0 (1.35) 470 1 - 470 (1.27) 228 2 - 450 (1.24) 168 3 - 438 (1.21) |
!heap -flt s <Size>
- 用于按指定大小筛选并分析堆内存块的关键命令
- 显示指定大小(十六进制)的所有分配块地址,适用于排查特定大小的内存泄漏
1 2 3 4 5 |
0:000> !heap -flt s 1234 _HEAP @ 1cc0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0000000001ccaae0 0126 0000 [00] 0000000001ccab10 01234 - (busy) _HEAP @ 10000 |
定位泄露点
!heap -x <LeakAddr>
- 查找包含指定地址的堆
1 2 3 4 |
0:000> !heap -x 0000000001cd0190 Entry User Heap Segment Size PrevSize Unused Flags ------------------------------------------------------------------------------------------------------------- 0000000001cd0190 0000000001cd01a0 0000000001cc0000 0000000001cc0000 50 b0 0 free |
!heap -p -a <LeakAddr>
- 分析泄漏块的调用栈
查看堆段分布
!heap -m <HeapAddress>
- 显示堆的段(
Segment
)地址范围
- 显示堆的段(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
0:000> !heap -m 1cc0000 Index Address Name Debugging options enabled 1: 01cc0000 Segment at 0000000001cc0000 to 0000000001dbf000 (00027000 bytes committed) Flags: 08000002 ForceFlags: 00000000 Granularity: 16 bytes Segment Reserve: 00100000 Segment Commit: 00002000 DeCommit Block Thres: 00000400 DeCommit Total Thres: 00001000 Total Free Size: 00000162 Max. Allocation Size: 00007ffffffdefff Lock Variable at: 0000000001cc02c0 Next TagIndex: 0000 Maximum TagIndex: 0000 Tag Entries: 00000000 PsuedoTag Entries: 00000000 Virtual Alloc List: 01cc0110 Uncommitted ranges: 01cc00f0 01ce7000: 000d8000 (884736 bytes) FreeList[ 00 ] at 0000000001cc0150: 0000000001ce5b80 . 0000000001cd6e80 (11 blocks) Segment00 at 01cc0000: Flags: 00000000 Base: 01cc0000 First Entry: 01cc0740 Last Entry: 01dbf000 Total Pages: 000000ff Total UnCommit: 000000d8 Largest UnCommit:00000000 UnCommitted Ranges: (1) |
检查堆元数据完整性
!heap -validate
- 验证堆结构是否损坏(需启用页堆)
1 2 3 4 5 6 |
0:000> !heap validate Index Address Name Debugging options enabled 1: 01cc0000 Segment at 0000000001cc0000 to 0000000001dbf000 (00027000 bytes committed) 2: 00010000 Segment at 0000000000010000 to 0000000000020000 (00001000 bytes committed) |
动态修改调试选项
!heap -p -enable 0x1000
- 临时启用堆栈跟踪(仅当前会话有效)
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Spy++相关08/18
- ♥ Windows高级调试_调试器03/19
- ♥ Windows 核心编程 _ 用户模式:线程同步三07/23
- ♥ Bkwin一12/01
- ♥ Soui五05/30
- ♥ Soui八06/20