Windows崩溃转储文件的详细内存使用分析?

24
我们收到了一位客户发送的原生(完整)崩溃转储文件。在Visual Studio(2005)调试器中打开它后,我们发现崩溃是由realloc调用引起的,尝试分配一个大约10 MB的内存块。转储文件异常地大(1.5 GB,通常情况下不超过500 MB),因此我们得出结论,我们存在内存“泄漏”或失控的分配,这要么完全耗尽了进程的内存,要么至少使其碎片化程度足以导致realloc失败。(请注意,此realloc用于分配日志缓冲区,我们并不惊讶它在这里失败了,因为10 MB一次性分配是我们除了某些非常大且不能更改的缓冲区之外进行的最大分配之一——问题本身可能与该特定分配无关。) 编辑:在与Lex Li的评论交换之后,我应该补充说明:这对我们来说是不可再现的(目前)。这只是一个明显显示了失控内存消耗的客户端转储。

主要问题:

现在我们有一个转储文件,但如何找出导致过量内存使用的原因?

目前我们所做的:

我们使用DebugDiag工具分析了转储文件(所谓的内存压力分析仪),以下是我们得到的结果:

Report for DumpFM...dmp

Virtual Memory Summary
----------------------
Size of largest free VM block   62,23 MBytes 
Free memory fragmentation       81,30% 
Free Memory                     332,87 MBytes   (16,25% of Total Memory) 
Reserved Memory                 0 Bytes   (0,00% of Total Memory) 
Committed Memory                1,67 GBytes   (83,75% of Total Memory) 
Total Memory                    2,00 GBytes 
Largest free block at           0x00000000`04bc4000 

Loaded Module Summary
---------------------
Number of Modules       114 Modules 
Total reserved memory   0 Bytes 
Total committed memory  3,33 MBytes 

Thread Summary
--------------
Number of Threads       56 Thread(s) 
Total reserved memory   0 Bytes 
Total committed memory  652,00 KBytes 

这只是为了提供一些背景信息。我认为更有趣的是:

Heap Summary
------------
Number of heaps         26 Heaps 
Total reserved memory   1,64 GBytes 
Total committed memory  1,61 GBytes 

Top 10 heaps by reserved memory
-------------------------------
0x01040000           1,55 GBytes        
0x00150000           64,06 MBytes        
0x010d0000           15,31 MBytes        
...

Top 10 heaps by committed memory
--------------------------------                              
0x01040000       1,54 GBytes 
0x00150000       55,17 MBytes 
0x010d0000       6,25 MBytes  
...            

现在,看着堆栈0x01040000(1.5 GB),我们可以看到:

Heap 5 - 0x01040000 
-------------------
Heap Name          msvcr80!_crtheap 
Heap Description   This heap is used by msvcr80 
Reserved memory      1,55 GBytes 
Committed memory     1,54 GBytes (99,46% of reserved)  
Uncommitted memory   8,61 MBytes (0,54% of reserved)  
Number of heap segments             39 segments 
Number of uncommitted ranges        41 range(s) 
Size of largest uncommitted range   8,33 MBytes 
Calculated heap fragmentation       3,27% 

Segment Information
-------------------
Base Address | Reserved Size   | Committed Size  | Uncommitted Size | Number of uncommitted ranges | Largest uncommitted block | Calculated heap fragmentation 
0x01040640        64,00 KBytes      64,00 KBytes   0 Bytes            0                              0 Bytes                     0,00% 
0x01350000     1.024,00 KBytes   1.024,00 KBytes   0 Bytes            0                              0 Bytes                     0,00% 
0x02850000     2,00 MBytes       2,00 MBytes       0 Bytes            0                              0 Bytes                     0,00% 
...

这个“段信息”具体是什么?

看一下所列举的分配情况:

Top 5 allocations by size
-------------------------
Allocation Size - 336          1,18 GBytes     
Allocation Size - 1120004      121,77 MBytes    
...

Top 5 allocations by count
--------------------------
Allocation Size - 336    3760923 allocation(s) 
Allocation Size - 32     1223794 allocation(s)  
...
我们可以看到,显然MSVCR80堆在336字节处持有3,760,923个分配。这表明我们用许多小的分配清理了内存,但是我们如何获取关于这些分配来自哪里的更多信息呢
如果我们能够采样其中一些分配地址,然后检查进程映像中这些地址的使用情况,那么 - 假设大部分这些分配都是造成我们的“泄漏” - 我们可能会找出这些失控分配来自哪里。
不幸的是,我目前真的不知道如何从转储中获得更多信息。 我该如何检查此堆以查看一些“336”分配地址? 我如何在转储中搜索这些地址,然后如何找出转储中哪个指针变量(如果有)保存了这些地址? 任何有关DebugDiag、WinDbg或其他工具的使用提示都可以帮助!如果您对我上面的分析有任何异议,请告诉我们!谢谢!

2
非常好的问题,感谢提供信息和步骤说明,我也遇到了类似的问题。顺便说一下,DebugDiag现在可以在http://www.microsoft.com/en-us/download/details.aspx?id=40336下载。 - x29a
1
我刚刚注意到上述提到的2.0版本不支持分析,因此应该获取1.2版本:http://www.microsoft.com/en-us/download/details.aspx?id=26798 - 如果安装失败,请创建一个名为“Users”的用户组。 - x29a
1
再次更新,2.x版本确实支持分析,只是将DebugDiag拆分为多个应用程序,即DebugDiag.Analysis.exe。此外,2.1版本现在可以在http://www.microsoft.com/en-us/download/details.aspx?id=42933上下载。 - x29a
3个回答

13
您可以:
  • 检查这些336字节的块,看看它们的内容是否能告诉您有关分配它们的内容的任何信息。为此,我通常使用windbg。首先运行命令!heap -stat -h 0x01040000,这将为您提供块的大小,然后将此size传递给!heap -flt s size,这将列出所有该大小的块。然后您可以使用显示内存的任何命令(如dc)查看块。
  • 如果您无法重现问题,则可以查看另一个转储文件,以查看分配那些大小的块的内容。首先使用gflags.exe 实用程序激活堆栈回溯功能(gflags -i your.exe +ust)。然后运行应用程序、获取一个转储文件并使用!heap -flt s 列出块。 接着,命令!heap -p -ablockaddress将转储分配块的函数调用堆栈。

6
你的第一个提示让我们找对了方向:!heap -stat 显示了我在问题中提到的 0x150 块。随后使用 !heap -flt s150 命令,输出了一长串地址。检查其中一些用户块地址,发现 df ... 显示了有效且一致的浮点数值。因此,我们知道是某些浮点数组泄漏导致程序出现问题,并成功跟踪到了这个泄漏源头。 - Martin Ba
我应该早点看到你的回答。之前我一直被!heap -flt s给封锁了,因为这个命令不会返回任何堆栈回溯信息。但是直到看到你的回答之前,我并不知道只有在使用gflags.exe(gflags -i your.exe +ust)激活堆栈回溯特征后采取转储后,!heap -flt s命令才会显示堆栈回溯信息。非常感谢,真的很有帮助。 - Vivek Raja

4
在windbg中,你可以尝试使用!heap -l命令来遍历堆(可能需要一些时间,可以通过限制搜索特定的堆来加快速度),并找到所有未被引用的繁忙块。然后打开内存窗口(alt+5),查看与你怀疑是泄漏的分配大小匹配的条目。幸运的是,可能会有一些常见的模式,可以帮助你识别数据或更好的是一些ASCII字符串,可以立即放置。
不幸的是,我不知道任何其他好的方法,除了在打开gflags的用户模式堆栈跟踪时尝试重现它,并使用umdh获取内存快照。

3

现在你有多少个转储文件?

跟踪内存泄漏的正确方法是充分利用DebugDiag的“内存和句柄泄漏规则”。

然后,当DebugDiag处理新的转储文件时,可以更多地了解内存使用情况。


我们只有一个转储文件。// “DebugDiag的内存和句柄泄漏规则的良好使用”是什么意思?如果有更多的转储文件,我还有哪些其他选项? - Martin Ba
http://blogs.msdn.com/b/tess/archive/2010/01/14/debugging-native-memory-leaks-with-debug-diag-1-1.aspx - Lex Li
谢谢,你提供的链接文章[使用Debug Diag 1.1调试本地内存泄漏][http://blogs.msdn.com/b/tess/archive/2010/01/14/debugging-native-memory-leaks-with-debug-diag-1-1.aspx]非常有用。不幸的是,当我们只有一个未启用诊断的转储文件时,它并没有太大帮助。 - Martin Ba
@Martin,在DebugDiag中使用泄漏规则重新捕获转储文件。您可以仔细配置泄漏规则,以便为您生成多个转储文件。尝试一下,你会看到效果的。 - Lex Li

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接