【问题标题】:Making sense of Performance Profiler output for a leaky application?了解泄漏应用程序的性能分析器输出?
【发布时间】:2020-05-11 19:32:06
【问题描述】:

我正在寻找 .NET 项目中的内存和资源泄漏,因为应用程序最终会因一些一般性 GDI+ 异常和有时 OOM 错误而崩溃。我试图分析的库使用 System.Graphics API,但不直接绘制到 GUI。我正在使用 Visual Studio 2019 企业版和内置的性能分析器工具来分析内存使用情况。该工具设置为捕获托管堆和本机堆使用情况。我拍了两张快照。第一个是尚未调用库时的基线。第二个快照是在循环中重复调用库方法并最终调用GC.Collect()GC.WaitForPendingFinalizers() 之后。下面显示的是内存使用图的屏幕截图。第一个显示托管堆,第二个显示本机堆。

分析器输出显示托管堆

分析器输出显示本机堆

涉嫌泄漏后的进程资源管理器条目

WinDbg中内存转储分析

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                    891     7ffd`bb247000 ( 127.991 TB)           99.99%
<unknown>                               916        2`3b96d000 (   8.931 GB)  98.40%    0.01%
Image                                   471        0`0837e000 ( 131.492 MB)   1.41%    0.00%
Heap                                     35        0`007e4000 (   7.891 MB)   0.08%    0.00%
Stack                                    21        0`00700000 (   7.000 MB)   0.08%    0.00%
Other                                     8        0`001cb000 (   1.793 MB)   0.02%    0.00%
TEB                                       7        0`0000e000 (  56.000 kB)   0.00%    0.00%
PEB                                       1        0`00001000 (   4.000 kB)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                             944        2`395e9000 (   8.896 GB)  98.02%    0.01%
MEM_IMAGE                               491        0`08a1a000 ( 138.102 MB)   1.49%    0.00%
MEM_MAPPED                               24        0`02da6000 (  45.648 MB)   0.49%    0.00%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                891     7ffd`bb247000 ( 127.991 TB)           99.99%
MEM_COMMIT                             1379        1`287f4000 (   4.633 GB)  51.05%    0.00%
MEM_RESERVE                              80        1`1c5b5000 (   4.443 GB)  48.95%    0.00%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                         1010        1`1e71f000 (   4.476 GB)  49.31%    0.00%
PAGE_EXECUTE_READ                        52        0`05ec3000 (  94.762 MB)   1.02%    0.00%
PAGE_READONLY                           180        0`032ca000 (  50.789 MB)   0.55%    0.00%
PAGE_WRITECOPY                          113        0`00ed6000 (  14.836 MB)   0.16%    0.00%
PAGE_EXECUTE_READWRITE                   17        0`00051000 ( 324.000 kB)   0.00%    0.00%
PAGE_READWRITE|PAGE_GUARD                 7        0`00021000 ( 132.000 kB)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                      1`3dbe9000     7ff2`c85d7000 ( 127.948 TB)
<unknown>                              7ff4`06370000        1`00020000 (   4.000 GB)
Image                                  7ffd`63eba000        0`0103d000 (  16.238 MB)
Heap                                      0`1ded2000        0`0017d000 (   1.488 MB)
Stack                                     0`1bac0000        0`000fa000 (1000.000 kB)
Other                                     0`017d0000        0`00181000 (   1.504 MB)
TEB                                       0`00e0d000        0`00002000 (   8.000 kB)
PEB                                       0`00e0c000        0`00001000 (   4.000 kB)

让我感到困惑的是,图表中的内存使用量(私有字节)不断增长(高达 5 GB),但快照 #2 仅显示大约 2.55 MB 的本机堆和大约 316 KB 的托管堆使用量。库函数实际上被调用了大约 400 次,只是为了加剧问题。 Private Bytes 使用量与循环计数成正比。尽管在每次迭代之后,由库方法返回的对象正在被处理,但内存使用量永远不会达到稳定状态,并且如果循环计数增加,内存使用量会继续增加。这和最终的 GDI+ 异常向我表明该库正在泄漏​​句柄或内存,但探查器输出似乎并未表明这一点。如果有人能对此有所了解并帮助我理解我在这里看到的内容,我会很高兴。

【问题讨论】:

  • 分析器跟踪“堆”,即 Microsoft 堆管理器和“托管堆”,即 .NET 的内存。该库似乎直接使用VirtualAlloc(),因此绕过了这两种堆类型。这对于图形应用程序来说是正常的,因为它们经常处理不需要优化处理的大数据(图片/图像)(VirtualAlloc 以 64kB 的块运行)。但是,当然,泄漏该内存是不正常的。它是什么图书馆?你确定你的处置正确吗?
  • @ThomasWeller 我设法解决了这个问题,但感谢您的观察结果证明是正确的。我使用了 SysInternals 的 vmmap 工具,并且能够查看泄漏块的列表。有数百个相同大小的块。块的数量与我的循环数成正比。这暗示了一些与图像相关的泄漏。有了这个线索,我最终要在执行 Bitmap.LockBits 后找到一个丢失的 UnlockBits 调用。

标签: c# .net performance memory-leaks windbg


【解决方案1】:

正如@ThomasWeller 指出的那样,事实证明这是图像数据的泄漏。以为我会在这里发布我的发现作为答案,希望它可以帮助某人。

在使用 WinDbg 一段时间后,我仍然找不到任何可以帮助我解决此漏洞的东西,这可能是由于我缺乏使用该工具的专业知识。然后我遇到了使用 SysInternals 的 VMMap 实用程序的建议。我发现这更容易使用,但显然不如 WinDbg 强大。下图是泄露发生后VMMap的截图。

可以看出,堆使用量只有大约 8 MB,而私有数据大约是 9 GB。单击私有数据条目会显示数百个相同大小的分配条目。鉴于库的性质,这表明它是某种图像数据。我的第一个怀疑是 Bitmap 对象没有被处理,但是查看代码中 Bitmap 的每次使用都没有发现任何东西。但是,在查看此内容时,我发现对 Bitmap.LockBits 的调用没有相应的 UnlockBits 调用。事实证明这是泄漏的原因。

【讨论】:

    猜你喜欢
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2022-10-08
    • 2010-11-10
    • 2011-03-03
    • 2010-10-20
    相关资源
    最近更新 更多