I'm seeing an apparent memory leak with a dotnet core 3.1 application in our Linux (Ubuntu 20.04) environment, but not our Windows (2012 R2) environment. I have run the same operations between the two, and it's clear that Linux is hanging onto memory (gigs worth) and Windows is properly disposing of it.
I've tried looking at dotnet core settings, (workstation vs server garbage collection, etc) and the defaults seem to be appropriate. I have tried researching the issue and have found a number of seemingly related posts here and elsewhere, but nothing conclusive or anything that has been helpful. Here are the most relevant posts I found:
- Dotnet Core Docker Container Leaks RAM on Linux and causes OOM
- How do I use server garbage collection with dotnet fsi?
- https://github.com/dotnet/diagnostics/issues/1402
I have also tried more general memory troubleshooting using MS's official documentation and the dotnet-counters tool. "Working set" memory appeared to be what is getting bloated, the GC Heap didn't expand much and relinquished what it did find.
In code, what's happening is I am creating images using System.Drawing, the images are wrapped in "using" blocks, and I am calling dispose on those images when done.
Code example - in summary, this function takes a user's imported image, and scales them down to a more optimized size. The helper functions "scaleImage" & "imageToByteArray" are the items in question
UserImage[] userImages = this._repository.getImagesForUsersByIds(userIds.ToArray(), idList.ToArray());
List<UserImage> returnImages = new List<UserImage>();
foreach (UserImage img in userImages) {
try {
string filePath = this._fileManager.getFilePathOfEditorImage(img.OwnerId, img.Id, "." + img.ImageType);
if (!string.IsNullOrEmpty(filePath) && this._fileInfoHelper.doesFileExist(filePath)) {
System.Drawing.Image scaledImage;
if (size > 0) {
scaledImage = this._fileInfoHelper.scaleImage(filePath, size * size);
} else {
scaledImage = System.Drawing.Image.FromFile(filePath);
}
byte[] bytes = this._fileInfoHelper.imageToByteArray(scaledImage, this._fileInfoHelper.getFormatFromExtension(img.ImageType));
string type = this._fileInfoHelper.getContentType(filePath);
img.Base64 = "data:" + type.ToLower() + ";base64," + Convert.ToBase64String(bytes);
returnImages.Add(img);
scaledImage.Dispose();
}
}
catch (Exception ex) {
//could not convert image....
}
}
return Ok(returnImages.ToArray());
Code example for an image scaling function I'm using:
public Image scaleImage(string fileName, int scaleImageArea) {
Image returnVal = null;
using (var bmpTemp = new Bitmap(fileName)) {
using (Image sourceImage = new Bitmap(bmpTemp)) {
if (sourceImage.Width * sourceImage.Height > scaleImageArea) {
//We need to scale the image down.
float width, height;
//This is the targetArea that we are shooting for.
float ratio = (float)sourceImage.Width / (float)sourceImage.Height;
//The expression to get the width is width^2 * ratio = target
height = (float)Math.Sqrt(scaleImageArea / ratio);
width = height * ratio;
returnVal = new Bitmap(sourceImage, new Size((int)width, (int)height));
} else {
returnVal = new Bitmap(sourceImage);
}
bmpTemp.Dispose();
sourceImage.Dispose();
}
}
return returnVal;
}
Code example of image to byte array function:
public byte[] imageToByteArray(System.Drawing.Image imageIn, System.Drawing.Imaging.ImageFormat format) {
byte[] returnValue;
using (var ms = new MemoryStream()) {
imageIn.Save(ms, format);
imageIn.Dispose();
returnValue = ms.ToArray();
}
return returnValue;
}
Specific problem: I have a memory leak in a Linux environment but not in windows with the same code. I suspect a dotnet core or other Linux-specific configuration/environment issue, but troubleshooting with official dotnet core memory leak tools has been a dead end.
Desired behavior: I don't want the memory leak and would expect to see behavior in Linux similar to that of Windows.