c# - 为什么 FREE block 在堆中这么大

标签 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)

为什么空闲 block 这么大?在摘要中,是否存在内存泄漏的任何线索?

最佳答案

您描述的是一个常见问题,不仅适用于测试人员,也适用于开发人员。在了解整个情况之前,您永远不知道有多少内存是真正空闲的。

我用酒吧老板的类比来解释这个问题。那个酒保订婚参加一个非常盛大的派对。他没有足够的眼镜,所以他联系了一家眼镜租赁公司。那家玻璃租赁公司有 8000 副眼镜要租。

酒保订购了 2000 杯。聚会开始时,所有的杯子都是空的。但是谁能给这个说法呢?从眼镜租赁公司的角度来看,2000 副眼镜已经用完了——不能再订购了。因此,玻璃租赁公司将其视为“正在使用”,而酒保则将其视为“未使用”。

派对开始,服务员接单,酒保调酒。在某个时间点,酒保给了服务员 500 杯。在他看来,1500 个杯子是空的,500 个是满的。

但是,这 500 杯可能还不够。有些客人可能已经非常口渴,已经喝光了杯子。所以实际上,这 500 个杯子中可能有 200 个是空的,只有 300 个杯子是满的。

所以,这取决于你问的是谁:

  • 眼镜租赁公司:租了2000副眼镜
  • 酒保:500 杯已送出,未归还
  • 客人:300个杯子满了

同样适用于程序的虚拟内存,只是参与者不同:

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

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

但是,您不知道 .NET 说的是什么。你需要问它。 !dumpheap -stat 是一个合适的问题。该答案将包括一些关于 Free 类型对象的陈述。

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

结论

您需要在正确的级别上提出正确的问题,以确定是否正在使用内存。

Why the Free block is so large?

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

Within the summary, any clue where memory leak could be?

泄漏由增长表示。您无法从单个快照中识别它。您需要随着时间的推移对其进行监控。哪个内存在增长,哪个没有增长? 不幸的是,无法从给定的信息中得出任何结论。

关于c# - 为什么 FREE block 在堆中这么大,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51440591/

相关文章:

C# 阻塞等待响应

C++:在 Windows 上使用 C++ 读写 BMP 文件的最简单方法是什么?

带有基本 jquery ui 插件的 IE6 中的 javascript 内存泄漏

c++ - 单个进程中的多个 log4cxx Dom 配置器

iphone - 内存警告并崩溃: how to handle it

java - 除了堆转储和线程转储之外,查找 java.exe 进程的内容

c# - Visual Studio 2012 项目总是过时(.cs 已修改)

c# - 获取Azure表中每个分区的第一行

c# - FormView EditTemplate 如何更新 ObjectDataSource UpdateParameters 中的值?

c++ - 某个范围内的随机数 c++