'Memory Leak when Resizing Image before saving it to server C# .NET
I am storing uploaded image file (IFormFile) using MemoryStream and FileStream.
When using solution like this, memory usage seems to be OK:
//projectFile is of type IFormFile
//without resize leak is not present
using (MemoryStream memoryStream = new MemoryStream())
{
await projectFile.CopyToAsync(memoryStream);
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
byte[] bA = memoryStream.ToArray();
memoryStream.Dispose();
await fs.WriteAsync(bA, 0, bA.Length);
fs.Close();
bA = null;
fs.Dispose();
}
}
However, when I attempt to resize the image before storing it, the ProcessMemory usage in Diagnostic Tools of Visual Studio increases some 100MB, even that file is small jpeg, and memory is not recovered whatsoever:
using (MemoryStream memoryStream = new MemoryStream())
{
await projectFile.CopyToAsync(memoryStream);//projectFile is of type IFormFile
using(FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
using (var img = Image.FromStream(memoryStream))
{
//memoryStream.Dispose();
var resized = new Bitmap(img, ImageManipulation.ResizeKeepAspect(img.Size, 1900, 2534, false));
using(MemoryStream memoryStream2 = new MemoryStream())
{
resized.Save(memoryStream2, System.Drawing.Imaging.ImageFormat.Jpeg);
//resized.Dispose();
//resized = null;
byte[] bA = memoryStream2.ToArray();
//memoryStream2.Dispose();
await fs.WriteAsync(bA, 0, bA.Length);
//fs.Close();
//bA = null;
//fs.Dispose();
}
}
}
}
and the ResizeKeepAspect is simply this:
public static Size ResizeKeepAspect(this Size src, int maxWidth, int maxHeight, bool enlarge)
{
maxWidth = enlarge ? maxWidth : Math.Min(maxWidth, src.Width);
maxHeight = enlarge ? maxHeight : Math.Min(maxHeight, src.Height);
decimal rnd = Math.Min(maxWidth / (decimal)src.Width, maxHeight / (decimal)src.Height);
return new Size((int)Math.Round(src.Width * rnd), (int)Math.Round(src.Height * rnd));
}
- Why memory is not recovering, and how can I modify second example to resize and recover memory afterwards?
- Why resizing the Image claims around 100MB of Process Memory, even that the file is simple screenshot?
----------------- EDIT -------------------- code looks like this after suggestions, still having problem with large memory allocation, but it seems to take less memory than before. I have used suggestions to not use byte[] and copy the memoryStream directly to fileStream. I would like to know, if there is still some space to save some more memory while resizing Image. As I have stated in the beginning, storing image without generating resized copy does not use all this memory. Shame it is not possible to purge these resources straight away.
Here is the updated code, still welcoming suggestions how to improve memory performace here:
using (MemoryStream memoryStream = new MemoryStream())
{
await projectFile.CopyToAsync(memoryStream);
using (var img = Image.FromStream(memoryStream))
{
memoryStream.Dispose();
using (Bitmap resized = new Bitmap(img, ImageManipulation.ResizeKeepAspect(img.Size, 1900, 2534, false)))
{
img.Dispose();
using (MemoryStream memoryStream2 = new MemoryStream())
{
resized.Save(memoryStream2, System.Drawing.Imaging.ImageFormat.Jpeg);
memoryStream2.Position = 0;
resized.Dispose();
using (var fs = new FileStream(filePath, FileMode.Create))
{
memoryStream2.CopyTo(fs);
fs.Close();
memoryStream2.Dispose();
fs.Dispose();
}
}
}
}
Solution 1:[1]
- Why memory is not recovering, and how can I modify second example to resize and recover memory afterwards?
When ASP.NET Core deal with high memory request, it keep the allocated memory. Thus it can deal the next similar high memory request without ask high amount of memory at OS (what is slow). It called the garbage collector server mode.
More information in : Memory management and garbage collection (GC) in ASP.NET Core
You can disable this behavior in .csproj :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
</Project>
Other method is explained in : Runtime configuration options for garbage collection
Try to disable the garbage collector mode and retest. If the memory is released, it isn't a memory leak, just a normal behavior.
- Why resizing the Image claims around 100MB of Process Memory, even that the file is simple screenshot?
using (MemoryStream memoryStream = new MemoryStream())
{
// First duplication in memory
await projectFile.CopyToAsync(memoryStream);//projectFile is of type IFormFile
using(FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
{
//Second duplication in memory
using (var img = Image.FromStream(memoryStream))
{
// Third duplication in memory and uncompressed
var resized = new Bitmap(img, ImageManipulation.ResizeKeepAspect(img.Size, 1900, 2534, false));
using(MemoryStream memoryStream2 = new MemoryStream())
{
resized.Save(memoryStream2, System.Drawing.Imaging.ImageFormat.Jpeg);
// Fourth duplication in memory
byte[] bA = memoryStream2.ToArray();
await fs.WriteAsync(bA, 0, bA.Length);
}
}
}
}
I use very a little the stream, I am not sure. But I think the image is duplicated in memory several times. More over it is in Debug, the memory usage is raw (not optimized).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | vernou |
