Out of memory exception while loading images

2019-02-19 02:49发布

问题:

I am using the following piece of code to load images as thumbnails to a FlowLayoutPanel control. Unfortunately i get an OutOfMemory exception.

As you already guess the memory leak is found at line

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

So how could i optimize the following code?

 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

Update: The problem occurs while loading a number of images (3264x2448px at 300dpi - each image is about 3Mb's)

回答1:

You can solve this in a few steps:

  • to get free from the File-dependency, you have to copy the images. By really drawing it to a new Bitmap, you can't just copy it.
  • since you want thumbnails, and your source-bitmaps are rather large, combine this with shrinking the images.


回答2:

Documentation for Image.FromFile (which is related to your FromStream) says that it will throw OutOfMemoryException if the file is not a valid image format or if GDI+ doesn't support the pixel format. Is it possible you're trying to load an unsupported image type?

Also, documentation for Image.FromStream says that you have to keep the stream open for the lifetime of the image, so even if your code loaded the image you'd probably get an error because you're closing the file while the image is still active. See http://msdn.microsoft.com/en-us/library/93z9ee4x.aspx.



回答3:

Couple of thoughts:

First off, as Jim has stated, when using Image.FromStream the stream should remain open for the lifetime of the Image as remarked on the MSDN page. As such, I would suggest to copy the contents of the file to a MemoryStream, and use the latter to create the Image instance. So you can release the file handle asap.

Secondly, the images you're using are rather big (uncompressed, as they would exist in memory, Width x Height x BytesPerPixel). Assuming the context you use them in might allow for them to be smaller, consider resizing them, and potentially caching the resized versions somewhere for later use.

Lastly, don't forget to Dispose the image and the Stream when they are no longer needed.



回答4:

I had the same problem. Jim Mischel answer led me to discover loading an innocent .txt file was the culprit. Here's my method in case anyone is interested.

Here's my method:

/// <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;
}