【发布时间】: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