简体   繁体   中英

.Net Image resizing memory leak

I am trying to resize images in a batch job. When I use the .Net provided classes, memory is not properly released so OutOfMemoryException is thrown. I think I use using statements properly. The code is below:

    private static byte[] Resize(byte[] imageBytes, int width, int height)
    {
            using (var img = Image.FromStream(new MemoryStream(imageBytes)))
            {
                using (var outStream = new MemoryStream())
                {
                    double y = img.Height;
                    double x = img.Width;

                    double factor = 1;
                    if (width > 0)
                        factor = width / x;
                    else if (height > 0)
                        factor = height / y;

                    var imgOut = new Bitmap((int)(x * factor), (int)(y * factor));
                    var g = Graphics.FromImage(imgOut);
                    g.Clear(Color.White);
                    g.DrawImage(img, new Rectangle(0, 0, (int)(factor * x),
                                                   (int)(factor * y)),
                                new Rectangle(0, 0, (int)x, (int)y), GraphicsUnit.Pixel);

                    imgOut.Save(outStream, ImageFormat.Jpeg);

                    return outStream.ToArray();
                }
            }
      }

Alternative to this code is to use FreeImage library. When I use FreeImage, there is no memory issue. Code with FreeImage:

   private static byte[] Resize(byte[] imageBytes, int width, int height)
   {
        var img = new FIBITMAP();
        var rescaled = new FIBITMAP();
        try
        {
            using (var inStream = new MemoryStream(imageBytes))
            {
                img = FreeImage.LoadFromStream(inStream);
                rescaled = FreeImage.Rescale(img, width, height, FREE_IMAGE_FILTER.FILTER_BICUBIC);

                using (var outStream = new MemoryStream())
                {
                    FreeImage.SaveToStream(rescaled, outStream, FREE_IMAGE_FORMAT.FIF_JPEG);
                    return outStream.ToArray();
                }
            }
        }
        finally
        {
            if (!img.IsNull)
                FreeImage.Unload(img);

            img.SetNull();

            if (!rescaled.IsNull)
                FreeImage.Unload(rescaled);

            rescaled.SetNull();
        }
   }

What is missing in my first code?

I believe your leak is with the following two lines:

var imgOut = new Bitmap((int)(x * factor), (int)(y * factor));
var g = Graphics.FromImage(imgOut);

Both Bitmap and Graphics implement IDisposable and should therefore be disposed when you are finished using them.

I would suggest wrapping them both in a using block:

using(imgOut = new Bitmap((int)(x * factor), (int)(y * factor)))
{
    using(var g = Graphics.FromImage(imgOut))
    {
        //rest of code...
    }
}

Here is a list of GDI objects to keep an eye out for, make sure you clean them up properly if you use them.

A more correct way:

private static byte[] Resize(byte[] imageBytes, int width, int height)
    {
        using (var imagestream = new MemoryStream(imageBytes))
        {
            using (var img = Image.FromStream(imagestream))
            {
                using (var outStream = new MemoryStream())
                {
                    double y = img.Height;
                    double x = img.Width;

                    double factor = 1;
                    if (width > 0)
                        factor = width / x;
                    else if (height > 0)
                        factor = height / y;

                    using (var imgOut = new Bitmap((int)(x * factor), (int)(y * factor)))
                    {
                        using (var g = Graphics.FromImage(imgOut))
                        {
                            g.Clear(Color.White);
                            g.DrawImage(img, new Rectangle(0, 0, (int)(factor * x),
                                                   (int)(factor * y)),
                                new Rectangle(0, 0, (int)x, (int)y), GraphicsUnit.Pixel);
                        }

                        imgOut.Save(outStream, ImageFormat.Jpeg);
                    }

                    return outStream.ToArray();
                }
            }
        }

 }

You also need to be very careful when allocating and deallocating large objects (ones >= 85000 bytes)...because they go on the LOH (Large Object Heap), and it is possible to fragment it and then run out of memory faster than you expect (there are various techniques to get around that if you run into that issue).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM