为什么FREE堆这么大

时间:2018-07-20 10:32:19

标签: c# c++ memory-leaks windbg

我有一个使用C#和WPF构建的应用程序,在Windows 8上运行。C#代码使用Interop调用外部C ++ dll。经过一系列操作,内存使用量高达2GB,因此怀疑存在内存泄漏。我创建了该过程的转储,并尝试使用WinDbg通过命令!address -summary对其进行分析,得到以下消息:

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    482      7ff`4d11b000 (   7.997 Tb)           99.97%
<unknown>                              1208        0`7573d000 (   1.835 Gb)  65.64%    0.02%
Heap                                   1664        0`1e1ab000 ( 481.668 Mb)  16.82%    0.01%
Image                                  1294        0`19f98000 ( 415.594 Mb)  14.52%    0.00%
Stack                                   261        0`053ce000 (  83.805 Mb)   2.93%    0.00%
Other                                    23        0`001d8000 (   1.844 Mb)   0.06%    0.00%
TEB                                      87        0`000ae000 ( 696.000 kb)   0.02%    0.00%
PEB                                       1        0`00001000 (   4.000 kb)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                            2634        0`922ae000 (   2.284 Gb)  81.69%    0.03%
MEM_IMAGE                              1787        0`1b38d000 ( 435.551 Mb)  15.21%    0.01%
MEM_MAPPED                               90        0`05807000 (  88.027 Mb)   3.07%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                509      7ff`4d1ae000 (   7.997 Tb)           99.97%
MEM_COMMIT                             3346        0`9205e000 (   2.282 Gb)  81.61%    0.03%
MEM_RESERVE                            1165        0`20de4000 ( 525.891 Mb)  18.37%    0.01%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                         1577        0`73295000 (   1.799 Gb)  64.36%    0.02%
PAGE_EXECUTE_READ                       183        0`1212e000 ( 289.180 Mb)  10.10%    0.00%
PAGE_READONLY                           941        0`082f9000 ( 130.973 Mb)   4.57%    0.00%
PAGE_READWRITE|PAGE_WRITECOMBINE         24        0`03aad000 (  58.676 Mb)   2.05%    0.00%
PAGE_EXECUTE_READWRITE                  131        0`00bcc000 (  11.797 Mb)   0.41%    0.00%
PAGE_READWRITE|PAGE_GUARD                87        0`00191000 (   1.566 Mb)   0.05%    0.00%
PAGE_NOACCESS                           399        0`0018f000 (   1.559 Mb)   0.05%    0.00%
<unknown>                                 1        0`00004000 (  16.000 kb)   0.00%    0.00%
PAGE_EXECUTE                              2        0`00003000 (  12.000 kb)   0.00%    0.00%
PAGE_WRITECOPY                            1        0`00002000 (   8.000 kb)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                     a1`9f910000      754`5f4fc000 (   7.330 Tb)
<unknown>                                a1`4fc30000        0`17ffc000 ( 383.984 Mb)
Heap                                     a1`1c421000        0`00dff000 (  13.996 Mb)
Image                                     0`6c991000        0`0174e000 (  23.305 Mb)
Stack                                    a1`160c0000        0`000fb000 (1004.000 kb)
Other                                    a1`4e480000        0`00181000 (   1.504 Mb)
TEB                                     7f5`fee0c000        0`00002000 (   8.000 kb)
PEB                                     7f5`ff14c000        0`00001000 (   4.000 kb)

为什么Free块这么大?在摘要中,有什么线索可能会导致内存泄漏?

2 个答案:

答案 0 :(得分:1)

您所描述的是一个常见问题,不仅对于测试人员,对于开发人员也是如此。在您了解整个图片之前,您永远都不知道真正有多少内存可用。

我用一个酒吧老板的比喻来解释这个问题。那个酒吧老板参加了一个非常大的聚会。他没有足够的眼镜,因此与玻璃租赁公司联系。那家玻璃租赁公司有8000副玻璃要出租。

酒吧老板定购2000杯。聚会开始时,所有眼镜都是空的。但是谁能发表这个声明呢?从玻璃租赁公司的角度来看,已经淘汰了2000杯-无法再订购。因此,玻璃租赁公司将它们视为“未使用”,而酒吧老板则将其视为“未使用”。

聚会开始,侍者接到命令,酒吧老板调酒。在某个时间点,酒吧老板给了500杯服务生。从他的角度来看,有1500杯是空的,有500杯是空的。

但是,这500杯可能还没装满。一些客人可能非常口渴,已经倒空了眼镜。因此,实际上,这500个眼镜中有200个可能是空的,只有300个杯子装满了。

所以,这取决于您问谁:

  • 眼镜租赁公司:租赁了2000副眼镜
  • 酒吧老板:500杯眼镜已经赠送,没有退还
  • 客人:300杯已满

程序的虚拟内存也是如此,只是参与者不同:

  • 玻璃租赁公司=操作系统
  • barkeeper =堆管理器,例如.NET堆管理器或Windows Heap管理器(C ++等)
  • 客人=应用逻辑

使用!address询问操作系统。如果它说“我为.NET分配了1.8 GB”,那么这是有助于理解现实的正确陈述。

但是,您不知道.NET怎么说。您需要问一下。 !dumpheap -stat将是一个适当的问题。该答案将包括一些有关Free类型的对象的声明。

即使您看到{。{1}}(被.NET视为“正在使用”),从应用程序逻辑的角度来看,这1 MB也可能是部分空白。如果那个byte[1000000]是一个缓冲区,那么通常会有一些byte[]属性告诉应用程序实际使用了多少缓冲区。其余的可以视为“免费”。

结论

您需要在正确的级别上询问正确的问题,以了解是否正在使用内存。

  

为什么Free块这么大?

因为那是玻璃租赁公司的答案。有很多眼镜可以租用(还剩6000个)。

  

在摘要中,有什么线索可能会导致内存泄漏?

泄漏表示增长。您无法从单个快照中识别它。您需要随时间监视它。哪个内存在增长,哪个不是? 不幸的是,无法根据给定的信息说什么。

答案 1 :(得分:0)

理论上,64位进程可以访问16 EB的虚拟内存,但是现代的CPU和OS通常会对此进行限制。 Windows允许寻址8 TB。请注意,无论计算机中有多少物理内存,所有64位进程都具有自己的专用8 TB虚拟内存块。 <unknown>块指示通过虚拟分配(除其他外)使用的内存,其中包括.NET内存。这是您提到的1.835 GB虚拟内存。兆兆字节为1,024兆字节,仅占8兆字节总可寻址虚拟内存的很小一部分。结果,自由块仍然很大。这在64位程序中非常典型,因为很少有程序使用多个TB的内存。尝试使用32位程序进行此实验,以查看截然不同的结果。