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?
Usage of memory profiler to detect a memory leak DotMemory, SciTech Memory Profiler, and ANTS Memory Profiler are the most popular.NET memory profilers. If you have Visual Studio Enterprise, you may also use a "free" profiler. Memory profilers all work in the same way.
If you have implemented a very long-running or infinite running thread that is not doing anything and it holds on to objects, you can cause a memory leak as these objects will never be collected. The fix for this is to be very careful with long-running threads and not hold on to objects not needed.
A memory leak may happen when your app references objects that it no longer needs to perform the desired task. Referencing said objects makes the garbage collector to be unable to reclaim the memory used, often resulting in performance degradation and potentially end up throwing an OutOfMemoryException.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With