【问题标题】:ImageList / Image OutOfMemoryExceptionImageList / Image OutOfMemoryException
【发布时间】:2009-06-12 03:08:40
【问题描述】:

在从 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。不幸的是,我能找到的唯一解决方案是每隔一段时间调用 GC.Collect()。

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


【解决方案1】:

你在处理graphics吗?此外,如果您像您提到的那样处理您的图像,那么您需要确保将其从 ImageList 中取出,否则您将导致更多问题。图片是什么格式的?

一般来说,当您在涉及图像时出现内存不足问题时,您的问题要么是某种方法不喜欢某种图像格式,要么是 9/10 次,您误解了其中一个图形对象的生命周期。

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

编辑:

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

【讨论】:

  • 感谢您的回复。这是关于图像处理的一个好点,我可能需要删除该行并离开垃圾收集。我不想 Dispose 图形对象,因为我从 EventArgs 中获取它,因此我不知道以后是否会在其他地方使用它。图像各不相同,我们发现它的时间是一个图标文件,所以我们使用 Icon.ToBitmap()。将尝试追踪内存管理器,这听起来非常有用。
  • 我只尝试过使用记忆工具。但是,似乎每次我调用 item.ImageList.Images[item.ImageKey] 时都会得到一个新图像,而不是对现有图像的引用。例如我可以做 item.ImageList.Images[item.ImageKey].Dispose() 然后调用 item.ImageList.Images[item.ImageKey] 并且仍然得到一个值。所以显得比较奇怪。到目前为止,它并没有帮助我解决问题。
  • 阅读 MSDN 上的 ImageCollection.Item 属性后,我发现返回了一个副本。在这种情况下,调用 image.Dispose() 是正确的方法。我猜是因为图像太多,绘图线程+事件正在消耗这个内存,而垃圾收集器在触发之前等待这个进程完成。如果我们有太多的图像,虽然它在这个过程发生之前就倒下了......
  • 我不确定上述更改是否有效,但是基于没有其他人提供任何东西并且我目前无法真正测试该技术的事实,我只是接受您的回答关闭线程...
  • 这是它应该将问题减半的事情之一,但我认为这可能还不够......
【解决方案2】:

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

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

我使用 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 2011-07-14
    • 2012-02-25
    • 2011-05-14
    • 2014-07-21
    • 1970-01-01
    • 2011-11-24
    相关资源
    最近更新 更多