我有一个内存转储文件,其中似乎有'System.Drawing.Bitmap'对象的内存泄漏。我有多个该类型的对象和!gcroot不能帮助我确定漏洞在哪里。
示例输出:
DOMAIN(0071D148):HANDLE(Pinned):1513e8:Root:03335250(System.Object[])-> 0248e8ec(System.Drawing.Bitmap)
我有一个想法,从内存转储中提取图像到一个图像文件中,然后,当我可以看到什么是泄漏的图像,我可以检查源代码周围的特定位图的创建。
那么,如何将内存写入一个文件,使我可以在图像查看器中打开并查看Bitmap对象保存的图像呢?
此外,如果你有其他的想法如何确定泄漏源,我很乐意听到他们。
谢谢
3条答案
按热度按时间1tuwyuhd1#
System.Drawing.Bitmap是一个很小的托管对象。它 Package 了一个由非托管GDI+ API返回的句柄,存储在私有 nativeImage 字段中。从该句柄中查找位图数据是一种大海捞针的练习。位图数据也不与图像文件格式兼容,只有Bitmap::保存()调用可以做到这一点,运行所需的图像编码器。
把这个想法划掉。
Bitmap对象的内存问题是非常常见的。太多的程序员忽略了Bitmap继承了IDisposable。你可以写很多.NET程序,但从来没有调用过Dispose()或使用 using 语句,程序运行得很好。垃圾收集者让他们远离麻烦。然而Bitmap类是一个单一的.NET类,它不能再工作了。问题是它太小了。在触发垃圾收集之前,您可以创建成千上万个垃圾。还不足以让垃圾回收器释放非托管GDI+句柄。因此,程序运行非常繁重,使用了大量非托管内存。当程序在32位模式下运行时,很可能会发生OOM崩溃。或在64位模式下提交大小为GB
在迷失在Windbg中之前,首先仔细检查程序的源代码。并验证是否可以将 every Bitmap变量与相应的Dispose()调用或 using 语句配对。注意一些事情,比如在分配PictureBox.Image属性时不使用调用前一个图像的Dispose()方法的代码。NET内存分析器是调试此问题的更好工具。
62lalag42#
如果你真的想做的话,dump all bitmaps from memory似乎仍然是可能的。
步骤:
!dumpheap -short -type System.Drawing.Bitmap
!do <address>
1.查找属性
nativeImage
的偏移量dps
(等于dps poi(<address>+<offset>)
?)给你类型dt <address> <type>
--> InternalBitmap属性dt <internalAddress> <internalType>
--> Bmp属性dt <bmpAddress> <bmpType>
提供Scan 0属性,即起始地址我只是不知道如何计算位图的大小,这将需要做一个
.writemem
。有宽度和高度,可能PixelFormat可以用来计算每个像素的位数。qni6mghb3#
感谢托马斯的参考dump all bitmaps from memory,我找到了一种将位图保存到文件的方法。
当我按照帖子中的步骤操作时,我在第5步遇到了一个问题,Symbol GdiPlus!找不到GpBitmap。
我在stackoverflow上发现了几篇有同样问题的文章。但没有答案如何解决这个问题。
保存图像数据的关键是找到原始数据的地址,即位图的Scan 0。
从帖子中,我们可以知道路线如下:位图对象地址-> nativeImage对象地址-> CopyOnWriteBitmap对象地址-> GpMemoryBitmapobject对象地址-> Scan 0地址。
虽然我在命令上有错误:dt 234fcaccb60 GdiPlus!GpBitmap,我仍然可以找到CopyOnWriteBitmap对象地址。并根据CopyOnWriteBitmap对象地址找出GpMemoryBitmap对象地址。最后找到Scan 0地址。因为内存布局是固定的。使用dt命令,我们可以很容易地看到CopyOnWriteBitmap对象地址和GpMemoryBitmapobject对象地址。但是没有dt命令,我们仍然可以根据帖子看到地址位置。
将图像数据保存到image.raw后,我们可以使用Photoshop打开它(输入宽度,高度,我们从最后一个dps命令中获得)并将其保存为bmp/png/jpg。