2

我有一个内存转储文件,其中似乎是“System.Drawing.Bitmap”对象的内存泄漏。我有多个该类型的对象, !gcroot 并不能帮助我确定泄漏的位置。

示例输出:

DOMAIN(0071D148):HANDLE(Pinned):1513e8:Root: 03335250(System.Object[])-> 0248e8ec(System.Drawing.Bitmap)

我有一个想法,将内存转储中的图像提取到图像文件中,然后,当我可以看到泄漏的图像是什么时,我可以检查围绕该特定位图的创建的源代码。

那么,如何将内存写入可以在图像查看器中打开并查看 Bitmap 对象保存的图像的文件中呢?

此外,如果您对如何识别泄漏源有其他想法,我很乐意听到。

谢谢

4

2 回答 2

5

System.Drawing.Bitmap 是一个很小的托管对象。它包装了一个由非托管 GDI+ api 返回的句柄,存储在私有nativeImage字段中。从该句柄中查找位图数据是大海捞针中的一项练习。位图数据也不以任何方式与图像文件格式兼容,只有 Bitmap::Save() 调用可以做到这一点,运行所需的图像编码器。

划掉这个想法。

否则,位图对象的内存问题常见。太多的程序员忽略了 Bitmap 继承了 IDisposable。您可以编写大量 .NET 程序,而从不调用 Dispose() 或使用using语句,程序运行良好。垃圾收集器让他们远离麻烦。然而,Bitmap 类是单一的 .NET 类,它不能再工作了。问题是它很小。您可以在触发垃圾收集之前创建数万个。几乎不足以让垃圾收集器释放非托管 GDI+ 句柄。结果,程序运行得非常重,使用大量非托管内存。当程序在 32 位模式下运行时,很可能发生 OOM 崩溃。或者在 64 位模式下提交大小为千兆字节

在迷失在 Windbg 之前,请先仔细查看程序的源代码。并验证您是否可以将每个Bitmap 变量与相应的 Dispose() 调用或using语句配对。请注意在没有代码的情况下分配 PictureBox.Image 属性之类的事情,该代码在前一个图像上调用 Dispose() 方法。.NET 内存分析器是更好的调试工具。

于 2013-11-14T11:46:03.220 回答
1

It seems still possible to dump all bitmaps from memory, if you really want to do it.

The steps:

  1. !dumpheap -short -type System.Drawing.Bitmap
  2. !do <address>
  3. Find offset of property nativeImage
  4. dps on that IntPtr (equal to dps poi(<address>+<offset>) ?) gives you the type
  5. dt <address> <type> --> InternalBitmap property
  6. dt <internalAddress> <internalType> --> Bmp property
  7. dt <bmpAddress> <bmpType> gives Scan0 property, which is the start address

I just couldn't follow on how to calculate the size of the bitmap, which would be needed to do a .writemem. There is width and height and probably PixelFormat could be used to calculate the bits per pixel.

于 2014-01-18T21:34:55.437 回答