加载图片时出现内存不足异常

3

我正在使用下面的代码将图片作为缩略图加载到FlowLayoutPanel控件中。不幸的是,我遇到了OutOfMemory异常。

正如你已经猜到的那样,内存泄漏的问题出现在以下代码行:

 Pedit.Image = System.Drawing.Image.FromStream(fs)

那么我该如何优化以下代码呢?
 Private Sub LoadImagesCommon(ByVal FlowPanel As FlowLayoutPanel, ByVal fi As FileInfo)
        Pedit = New DevExpress.XtraEditors.PictureEdit
        Pedit.Width = txtIconsWidth.EditValue
        Pedit.Height = Pedit.Width / (4 / 3)
        Dim fs As System.IO.FileStream
        fs = New System.IO.FileStream(fi.FullName, IO.FileMode.Open, IO.FileAccess.Read)
        Pedit.Image = System.Drawing.Image.FromStream(fs)
        fs.Close()
        fs.Dispose()
        Pedit.Properties.SizeMode = DevExpress.XtraEditors.Controls.PictureSizeMode.Zoom

        If FlowPanel Is flowR Then
            AddHandler Pedit.MouseClick, AddressOf Pedit_MouseClick
            AddHandler Pedit.MouseEnter, AddressOf Pedit_MouseEnter
            AddHandler Pedit.MouseLeave, AddressOf Pedit_MouseLeave
        End If

        FlowPanel.Controls.Add(Pedit)
    End Sub

更新:问题发生在加载大量图像时(每个图像为3264x2448像素,300dpi - 每个图像约为3MB)


你尝试过通过删除所有多余的代码并仅尝试加载图像来隔离问题吗?如果成功加载图像,你尝试将其分配到“正常”的图像显示控件中吗? - Grant Thomas
1
你是在加载一张图片时就耗尽了内存,还是在加载多张图片后才出现这种情况?是仅有某张图片导致问题,还是当你尝试加载任何图片时都失败了?这些图片特别大吗?请提供更多细节,否则我们只能猜测。 - Jim Mischel
@Chocol8,这种情况是发生在任何一组图像上还是只有一个特定的图像引起的?不幸的是,OOM异常往往是处理图像时经常抛出的通用错误。(好吧,不是通用错误,而是一个低级错误,它没有被包装在更具体的错误中。)我以前遇到过这个错误,那时是因为图像损坏了。有时候是将图像解压缩成内存中更大的图像。https://dev59.com/-3NA5IYBdhLWcg3wC5Xh - Chris Haas
@Chris。这个错误发生在一组图像上。 - OrElse
3
3264x2448分辨率的图像包含7,990,272个像素。如果它是24位彩色,那么每个像素需要3个字节,大约是23兆字节每张图片。当压缩在磁盘上时,它们可能只有3MB大小,但是在内存中它将使用完整的未压缩大小。如果你的系统内存很小,则无法加载许多这样的图片。 - Jim Mischel
4个回答

5
< p > Image.FromFile 的文档(它与您的FromStream相关)说明,如果文件不是有效的图像格式或GDI+不支持像素格式,则会引发OutOfMemoryException。您是否尝试加载不受支持的图像类型?

Image.FromStream 的文档还指出,您必须在图像的生命周期内保持流处于打开状态,因此即使您的代码加载了图像,由于您在图像仍然处于活动状态时关闭了文件,您可能仍会收到错误提示。请参见http://msdn.microsoft.com/en-us/library/93z9ee4x.aspx


4

几点想法:

首先,如Jim所述,使用Image.FromStream时,应按MSDN页面上所述,使流在图像的生命周期内保持打开状态。因此,建议将文件内容复制到MemoryStream中,并使用后者创建Image实例。这样就可以尽快释放文件句柄。

其次,您使用的图像相当大(未压缩,因为它们存在于内存中,宽度x高度x每像素字节数)。假设您使用它们的上下文可能允许它们更小,请考虑调整它们的大小,并可能将调整大小的版本缓存到某个地方以供以后使用。

最后,不要忘记在不再需要它们时处置图像和流。


3
您可以通过以下几个步骤解决这个问题:
  • 为了摆脱文件依赖性,您需要复制图片。您不能只是简单地复制它,而是要将其绘制到新的位图中。
  • 由于您想要缩略图,而您的源位图相当大,因此可以将其与缩小图像结合使用。

...然后我准备发布第三个相关问题...一个类似于"ACDSee"的程序是如何管理这个的? - OrElse
@chocol:我想ACDsee和类似的软件会创建缩略图并使用按需加载来处理其余部分。 - H H

0

我曾经遇到过同样的问题。Jim Mischel的答案让我发现,加载一个无辜的.txt文件是罪魁祸首。以下是我的解决方法,供有兴趣的人参考。

我的解决方法如下:

/// <summary>
/// Loads every image from the folder specified as param.
/// </summary>
/// <param name="pDirectory">Path to the directory from which you want to load images.  
/// NOTE: this method will throws exceptions if the argument causes 
/// <code>Directory.GetFiles(path)</code> to throw an exception.</param>
/// <returns>An ImageList, if no files are found, it'll be empty (not null).</returns>
public static ImageList InitImageListFromDirectory(string pDirectory)
{
    ImageList imageList = new ImageList();

    foreach (string f in System.IO.Directory.GetFiles(pDirectory))
    {
        try
        {
            Image img = Image.FromFile(f);
            imageList.Images.Add(img);
        }
        catch
        {
            // Out of Memory Exceptions are thrown in Image.FromFile if you pass in a non-image file.
        }
    }

    return imageList;
}

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