ImageList / Image OutOfMemoryException

4

当从ImageList中获取图像时,我遇到了OutOfMemoryException的问题,我一直无法找到合适的解决方案。

我有一个自定义的ListView控件,它附加了一个事件用于绘制ListViewItems。然后调用一个静态方法来绘制该项。

对于大约300个项目的ListView,每次滚动ListView时,内存会增加约100Mb。问题代码已被追踪到以下内容:

Image image = item.ImageList.Images[item.ImageKey];
if (image != null)
{
    Size imageOffset = new Size((bounds.Width - image.Width) / 2, 2); 
    Point imagePosition = bounds.Location + imageOffset;
    graphics.DrawImageUnscaled(image, imagePosition);
}

似乎(特别是在WinXP上)垃圾回收机制没有正常工作,导致内存不断增长。我们尝试在代码块后直接添加image.Dispose()来解决这个问题,但没有任何效果。
目前我找到的唯一解决方案是在静态方法结束时调用GC.Collect()。然而,这个方法会导致ListView重新绘制自己,速度很慢,并且在重新绘制时屏幕上会出现伪像。
有人遇到过类似问题吗?或者知道解决方法?

我在两个不涉及图像或listView的应用程序中遇到了outofmemoryexceptions。不幸的是,我能找到的唯一解决方案就是定期调用GC.Collect()。 - Crispy
2个回答

4
您是否正在处理 graphics?同时,如果您像您提到的那样处理图像,则需要确保将其从ImageList中移除,否则会导致更多问题。这些图像的格式是什么?
通常涉及图像时,当出现内存不足问题时,您的问题要么是某个方法不喜欢某个图像格式,要么是您误解了一个图形对象的生命周期。
请执行以下操作:
- 检查所有使用的Graphics并将它们放在using块中。 - 检查您的Image生命周期,并小心复制、处理、关闭底层流等。 - 加载内存管理器(VS2008内置)并查看未清理干净的内容。
编辑:以下是我能找到的最佳选择:ImageList.Draw(graphics, x, y, width, height, index)。这将使用内部句柄而不是创建图像的副本。

谢谢回复。你提到的释放图像资源的问题很有道理,我可能需要删除那一行代码,只留下垃圾回收。我不想释放绘图对象,因为我是从 EventArgs 中获取它的,所以我不知道它是否会在其他地方被使用。图片类型各异,我们发现这个问题时正在使用一个图标文件,所以我们使用了 Icon.ToBitmap() 方法。我会尝试找到内存管理器,听起来非常有用。 - Ian
我只是尝试过一次使用内存工具。但是每次调用item.ImageList.Images[item.ImageKey]时,似乎都会得到一个新的图像,而不是对现有图像的引用。例如,我可以执行item.ImageList.Images[item.ImageKey].Dispose(),然后调用item.ImageList.Images[item.ImageKey],仍然可以得到一个值。所以看起来相当奇怪。然而,它还没有帮助我解决这个问题。 - Ian
在阅读了MSDN上的ImageCollection.Item属性后,我发现返回了一份副本。在这种情况下,调用image.Dispose()是正确的方法。我猜测,由于有太多的图像,绘图线程和事件正在消耗这些内存,而垃圾回收器在等待这些进程完成之前触发。然而,如果我们有太多的图像,在此进程发生之前,它就会崩溃... - Ian
我不确定上述更改是否有效,但基于没有其他人提供任何东西,而且我目前无法测试该技术,所以我只是接受你的答案来关闭这个线程... - Ian
这是其中一种情况,它应该能够将问题减少一半,但我认为这可能还不够... - JasonRShaver

0

我已经成功解决了我的应用程序中的这个问题。

Jason有答案,你必须确保使用“using”块或它们的等效物。

我使用VB,等效物是在创建新位图时使用Try...Catch...Finally,调用BitMap.Dispose并在“Finally”部分设置Bitmap = nothing。

从我花费的时间来看,这似乎是一个非常普遍的问题。下面的代码还允许任何图像在缩小为缩略图时保持其纵横比,这似乎也很难通过Google解决!

代码:

Private Function AspectedImage(ByVal ImagePath As String, ByVal SizeWanted As Integer) As Image

    Dim myBitmap, WhiteSpace As System.Drawing.Bitmap
    Dim myGraphics As Graphics
    Dim myDestination As Rectangle
    Dim MaxDimension As Integer
    Dim ReductionRatio As Double

    Try

        'create an instance of bitmap based on a file
        myBitmap = New System.Drawing.Bitmap(ImagePath)



        'create a new square blank bitmap the right size
        If myBitmap.Height >= myBitmap.Width Then MaxDimension = myBitmap.Height Else MaxDimension = myBitmap.Width
        ReductionRatio = SizeWanted / MaxDimension
        WhiteSpace = New System.Drawing.Bitmap(SizeWanted, SizeWanted)

        'get the drawing surface of the new blank bitmap
        myGraphics = Graphics.FromImage(WhiteSpace)

        'find out if the photo is landscape or portrait
        Dim WhiteGap As Double

        If myBitmap.Height > myBitmap.Width Then 'portrait
            WhiteGap = ((myBitmap.Width - myBitmap.Height) / 2) * -1
            myDestination = New Rectangle(x:=CInt(WhiteGap * ReductionRatio), y:=0, Width:=Int(myBitmap.Width * ReductionRatio), Height:=Int(myBitmap.Height * ReductionRatio))
        Else 'landscape
            WhiteGap = ((myBitmap.Width - myBitmap.Height) / 2)
            'create a destination rectangle
            myDestination = New Rectangle(x:=0, y:=CInt(WhiteGap * ReductionRatio), Width:=Int(myBitmap.Width * ReductionRatio), Height:=Int(myBitmap.Height * ReductionRatio))
        End If

        'draw the image on the white square
        myGraphics.DrawImage(image:=myBitmap, rect:=myDestination)
        AspectedImage = WhiteSpace

    Catch ex As Exception
        myBitmap = New System.Drawing.Bitmap("")
        AspectedImage = New System.Drawing.Bitmap(4, 4)
        ImageBufferExceeded = True
        MsgBox("Image Buffer exceeded, too many images in memory. If the one(s) you want can't be seen, please restart the application and navigate straight to your images")
    Finally
        myBitmap.Dispose()
        myBitmap = Nothing
        WhiteSpace = Nothing
    End Try

End Function

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接