使用WinDbg分析WPF应用程序中OutOfMemoryException的根本原因

使用WinDbg分析WPF应用程序中OutOfMemoryException的根本原因,wpf,debugging,memory-leaks,windbg,dump,Wpf,Debugging,Memory Leaks,Windbg,Dump,我在理解崩溃转储和查找WPF应用程序引发的OutOfMemoryException的根本原因方面遇到了一些困难。异常是在应用程序运行数小时后引发的,因此这清楚地表明存在内存泄漏 我的第一步是看!地址-摘要命令: --- Usage Summary ---------------- RgnCount ------- Total Size -------- %ofBusy %ofTotal <unknown> 2043

我在理解崩溃转储和查找WPF应用程序引发的
OutOfMemoryException
的根本原因方面遇到了一些困难。异常是在应用程序运行数小时后引发的,因此这清楚地表明存在内存泄漏

我的第一步是看
!地址-摘要
命令:

--- Usage Summary ---------------- RgnCount ------- Total Size -------- %ofBusy %ofTotal
<unknown>                              2043          58997000 (   1.384 Gb)  71.43%   69.22%
Heap                                    152           fcc3000 ( 252.762 Mb)  12.74%   12.34%
Image                                  1050           bc77000 ( 188.465 Mb)   9.50%    9.20%
Stack                                   699           7d00000 ( 125.000 Mb)   6.30%    6.10%
Free                                    518           3f6b000 (  63.418 Mb)            3.10%
TEB                                     125             7d000 ( 500.000 kb)   0.02%    0.02%
Other                                    12             36000 ( 216.000 kb)   0.01%    0.01%
PEB                                       1              1000 (   4.000 kb)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                            2186          685b7000 (   1.631 Gb)  84.14%   81.53%
MEM_IMAGE                              1710           f3f3000 ( 243.949 Mb)  12.29%   11.91%
MEM_MAPPED                              186           46db000 (  70.855 Mb)   3.57%    3.46%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT                             3366          73fe7000 (   1.812 Gb)  93.52%   90.62%
MEM_RESERVE                             716           809e000 ( 128.617 Mb)   6.48%    6.28%
MEM_FREE                                518           3f6b000 (  63.418 Mb)            3.10%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                         1650          5e19e000 (   1.470 Gb)  75.87%   73.52%
PAGE_EXECUTE_READ                       224           bc42000 ( 188.258 Mb)   9.49%    9.19%
PAGE_READWRITE|PAGE_WRITECOMBINE         28           439f000 (  67.621 Mb)   3.41%    3.30%
PAGE_READONLY                           573           3d7b000 (  61.480 Mb)   3.10%    3.00%
PAGE_WRITECOPY                          214            f8f000 (  15.559 Mb)   0.78%    0.76%
PAGE_EXECUTE_READWRITE                  265            d0a000 (  13.039 Mb)   0.66%    0.64%
PAGE_READWRITE|PAGE_GUARD               357            33b000 (   3.230 Mb)   0.16%    0.16%
PAGE_EXECUTE_WRITECOPY                   55            119000 (   1.098 Mb)   0.06%    0.05%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
<unknown>                                   78d40000           2350000 (  35.313 Mb)
Heap                                        36db0000            fd0000 (  15.813 Mb)
Image                                       64a8c000            e92000 (  14.570 Mb)
Stack                                        4b90000             fd000 (1012.000 kb)
Free                                        7752f000            1a1000 (   1.629 Mb)
TEB                                         7ede3000              1000 (   4.000 kb)
Other                                       7efb0000             23000 ( 140.000 kb)
PEB                                         7efde000              1000 (   4.000 kb)
请注意,共有64个段,每个段大约为(16MB)。似乎有一些数据保存在内存中,从未被释放

接下来我看一下
!dumpheap-stat

65c1f26c   207530     19092760 System.Windows.Media.GlyphRun
65c2c434   373991     20943496 System.Windows.Media.RenderData
68482bb0   746446     26872056 MS.Utility.ThreeItemList`1[[System.Double, mscorlib]]
65c285b4   746448     29857920 System.Windows.Media.DoubleCollection
64c25d58   299568     32353344 System.Windows.Data.BindingExpression
6708a1b8  2401099     38417584 System.WeakReference
67082c2c  1288315     41226080 System.EventHandler
67046f80  1729646     42238136 System.Object[]
64c1409c   206969     52156188 System.Windows.Controls.ContentPresenter
67094c9c   382163     64812664 System.Byte[]
004b0890      159     65181140      Free
64c150d0   207806     72316488 System.Windows.Controls.TextBlock
6708fd04  1498498     97863380 System.String
6848038c   847783    128775772 System.Windows.EffectiveValueEntry[]
004b0890      159     65181140      Free
据我所知,没有一个单一的对象会占用所有的内存。最大的一个只有大约122MB。将所有大小(8500行输出行)相加,得到占用内存的大小(1.1GB)。似乎所有的对象图都以某种方式被复制并添加到内存中,并且从未被释放

!gcroot 6848038c
!gcroot 6708fd04
检查ValueEntry和System.String的有效性,永不终止,堆栈太大

dumpheap-mt
没有向我展示与我相似的东西<代码>!FinalizeEQUEUE显示有许多对象(超过200万个)已注册以进行终结:

6708a1b8  2401099     38417584 System.WeakReference
Total 2417538 objects
我怀疑当应用程序试图复制对象图并分配新内存时会发生
OutOfMemoryException
,但我找不到它的根本原因

问题如何深入到问题的根源(我可以使用windbg的其他命令检查它)。似乎不止一个对象在泄漏,整个对象图也在泄漏。。我是在正确的轨道上,还是我忽略了别的什么?其他假设是什么?

对象图复制 您的应用程序使用.NET提供的约1.1 GB虚拟内存。您可以从
的输出中看到这一点!eeheap-gc
直接

GC Heap Size:            Size: 0x448fde94 (1150279316) bytes.
或者将
的值相加!dumpheap-stat

将所有尺寸(8500行输出线)相加得出(1.1GB)

这大致与
中显示为
的值相关!地址-摘要

--- Usage Summary ---------------- RgnCount ------- Total Size -------- %ofBusy %ofTotal
<unknown>                              2043          58997000 (   1.384 Gb)  71.43%   69.22%
不幸的是,这65 MB被分成159个较小的区域。要获得其中最大的块,您需要运行
!转储堆-mt 004b0890

此外,.NET还可以从Windows(从
!address-summary
)获得另外63 MB:

但是最大的数据块只有1.6MB,所以这几乎是无用的:

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                        7752f000            1a1000 (   1.629 Mb)
因此,很明显,应用程序内存不足

记忆在哪里? 252MB在本机堆中。似乎您正在使用一些本机DLL。虽然目前这似乎不太多,但这一事实可能表明存在固定对象。固定对象不会被垃圾收集。请看
的输出!gchandles
以确定这是否是问题的一部分

DLL中有188MB的内存。您可以卸载未使用的本机DLL,但对于.NET程序集,您可能对此无能为力

125MB是堆叠的。默认大小为1MB,应用程序中似乎有125个线程。使用
!clrstack
以了解他们正在做什么以及为什么他们还没有完成。可能每个线程都在处理某些事情,但尚未释放对象。如果您可以控制它,那么不要并行启动那么多线程。例如,仅使用8个螺纹,等待螺纹完成后再进行下一项工作

当然,大部分内存是由.NET对象使用的。然而,你得出了一些错误的结论

据我所知,没有一个对象占用了所有的内存。最大的一个只有大约122MB

请注意,没有一个对象占用122 MB ob内存。共有847.783人。这将问题从“为什么这个对象使用这么多内存?”更改为“为什么有这么多内存?”。例如,为什么应用程序需要207.806文本块?它真的显示了这么多文本吗

使用
!gcroot
是个好主意。但是,您将其用于方法表的地址,而不是对象:

!gcroot 6848038c
!gcroot 6708fd04
这两个数字都来自
的输出!dumpheap-stat
。在
中使用它们!gcroot
应该发出如下警告

请注意,6848038c不是有效的对象

相反,
!gcroot
仅适用于从
获取的单个对象!dumpheap
不带
-stat
参数

您可以使用
!traveseheap filename.log
将所有对象转储到与兼容的文件中。请注意,CLR探查器无法读取
-xml
格式。加载对象信息后,堆图可能是最有用的按钮


要查找OutOfMemoryException的触发器,可以使用
!u
命令。您需要阅读一些MSIL代码来了解它的功能。另见。然而,在您的场景中,我想这是没有用的,因为即使是小对象也可能触发它。

除了另一个答案,您可以尝试使用WinDbg扩展可视化内存地址和GC堆

也可以尝试使用CLR Profiler,它可以非常容易地为您提供大量复杂的信息。您可以使用
导出CLR探查器所需的日志!遍历EAP
。最重要的是,它使显示对象分配图变得非常容易。然而,我很震惊,这可能对你没有帮助-我认为WPF本身存在一些令人讨厌的内存泄漏,环顾四周可能会帮助你找到这个问题。@Luaan不幸的是,我只有崩溃转储,可以进行事后调试。无论如何,谢谢你对CLR提供程序的支持。是
!TraverseHeap
特定于CLR探查器的命令?您可以加载由
生成的文件!将EAP
遍历到CLR探查器中,因此崩溃
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                        7752f000            1a1000 (   1.629 Mb)
!gcroot 6848038c
!gcroot 6708fd04