ImageList / Image OutOfMemoryException

时间:2009-05-19 13:00:31

标签: c# winforms image out-of-memory

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

我有一个Custom 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缓慢地重新绘制自己,并且在尝试重新绘制时最终会在屏幕上出现伪像。

还有其他人经历过这个吗?或者知道解决方法?

2 个答案:

答案 0 :(得分:4)

你在处理graphics吗?此外,您按照提到的方式处理图像,然后您需要确保将其从ImageList中取出,否则会导致更多问题。图像的格式是什么?

一般情况下,当涉及图像时出现内存不足问题,您的问题将是某些方法不喜欢某种图像格式,或者9/10次,您误解了其中一个图形对象的生命周期。

  • 检查所有Graphics用法,并将其放入using块。
  • 检查Image生命周期,小心复制,处理它们,关闭底层流等等。
  • 加载一个内存管理器(VS2008内置一个内存管理器),看看哪些内容没有得到很好的清理。

编辑:

这是我能找到的最佳选择,使用ImageList.Draw(graphics, x, y, width, height, index)。这将使用内部句柄,而不是创建图像的副本。

答案 1 :(得分:0)

我已经设法在我的应用程序中解决了这个问题。

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

我使用VB,相当于使用Try ... Catch ...最后每当我创建一个新的位图时,调用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