I'm resizing heaps of images to 1000x1000 thumbnails in parallel and running out of memory very quickly. (Performance profiler puts me at 3GB of used memory after about 3 minutes)
Originally I was using Image.FromFile()
but doing some research, I found that Image.FromStream()
is the way to go. I think I have the appropriate using statements, something somewhere is still keeping stuff in memory and the GC isn't clearing resources as expected.
It seems like there's an issue with GDI+ keeping the handles open, but I can't seem to find an appropriate solution for my case.
Questions:
- Am I doing something completely wrong?
- If not, is there a better way to
Dispose()
of the stream / image / ResizedImage so I'm not eating up all the resources, while still maintaining speedy parallel operations? - If GDI+ is the problem and is keeping unmanaged resources alive, how do I correct the issue?
Code
List<FileInfo> files
contains ~300 valid JPG images, each JPG ~2-4mb
Caller
public void Execute()
{
Parallel.ForEach(Files, (file) =>
{
Resize.ResizeImage(file.FullName);
}
);
}
Execute()
calls a Parallel.Foreach()..
Resize Class
public static class Resize
{
public static void ResizeImage(string fileName)
{
ResizeImage(fileName, 1000, 1000, true);
}
public static void ResizeImage(string fileName, int newHeight, int newWidth, bool keepAspectRatio = true)
{
string saveto = Path.GetDirectoryName(fileName) + @"\Alternate\" + Path.GetFileName(fileName);
try
{
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
using (Image ImageFromStream = Image.FromStream(fs))
{
var ImageSize = new Size(newHeight, newWidth);
if (keepAspectRatio)
{
int oWidth = ImageFromStream.Width;
int oHeight = ImageFromStream.Height;
double pWidth = ((double)ImageSize.Width / (double)oWidth);
double pHeight = ((double)ImageSize.Height / (double)oWidth);
double percent;
if (pHeight < pWidth)
percent = pHeight;
else
percent = pWidth;
newWidth = (int)(oWidth * percent);
newHeight = (int)(oHeight * percent);
}
else
{
newWidth = ImageSize.Width;
newHeight = ImageSize.Height;
}
var ResizedImage = new Bitmap(newWidth, newHeight);
using (Graphics gfxHandle = Graphics.FromImage(ResizedImage))
{
gfxHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfxHandle.DrawImage(ImageFromStream, 0, 0, newWidth, newHeight);
if (!Directory.Exists(Path.GetDirectoryName(saveto))) { Directory.CreateDirectory(Path.GetDirectoryName(saveto)); }
ResizedImage.Save(saveto, ImageFormat.Jpeg);
}
ResizedImage.Dispose();
ResizedImage = null;
}
}
}
catch (Exception ex)
{
Debug.WriteLine(string.Format("Exception: {0}", ex.Message));
}
}