ImageList / Image OutOfMemoryException

从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缓慢地重新绘制自己,并且在尝试重新绘制时最终会在屏幕上获得工件。

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

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

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

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

编辑:

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

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

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