0

I am currently working on a .NET 9 WinForms program that contains an ImageViewer, which is a seperate window where you can scroll through a series of images.

My problem is that even after closing the form and taking care of all references to the images which were loaded like this:

ThingFile? imageFile = await ThingData.LoadFileAsync<ThingFile>(Images[Index].ID);
ArgumentNullException.ThrowIfNull(imageFile);

try
{
    if (ThingData.VerifyFile(imageFile) && imageFile.Content != null)
    {
        var imageData = imageFile.Content;

        imageFile.Clear();

        using var img = new MagickImage(imageData);
        if (img.ColorSpace != ColorSpace.sRGB)
        {
            img.TransformColorSpace(ColorProfile.SRGB);
        }

        using var ms = new MemoryStream();
        img.Write(ms, MagickFormat.Png32);
        ms.Position = 0;

        using var tempBitmap = new Bitmap(ms);
        pictureBox.Image = (Bitmap)tempBitmap.Clone();
        imageData = null;
    }
    else
    {
        ArgumentNullException.ThrowIfNull(pictureBox.ErrorImage);
        pictureBox.Image = (Image)pictureBox.ErrorImage.Clone();
    }
}
finally
{
    imageFile = null;
}

the allocated RAM is signifficantly bigger than before the whole procedure started. For example, after analysing memory usage with dotMemory, 60MB were used, the ImageViewer got used and closed, and (even after calling the GarbageCollector with GC.Collect(); ) the RAM had gone up to 100MB. The 40MB werent just not freed but lost, since doing the same thing again would end with the RAM being at 140, then 180 and so on...

5
  • How are you measuring memory usage? You should be using a memory profiler, since that will a) Do a GC before measuring, b) measure the actual usage, c) Show what your memory is used for. Just checking the task manager is not a good approach since that will just show how much memory the runtime has asked for, not the actual usage.. Commented Oct 21 at 6:56
  • Which version .NET? The behavior of the garbage collector has changed a lot over time. Is it WinForms? Is it System.Drawing.Bitmap? Why you do Bitmap.Clone() ? Commented Oct 21 at 7:52
  • The types you use have finalizers. In this case, the following code is used to completely free up memory: GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Commented Oct 21 at 8:03
  • Look here: Preparing for the .NET 10 GC (DATAS) - now requires 3 GC.Collect(). Please specify which .NET version you use, so that we can talk in a meaningful way. Commented Oct 21 at 8:09
  • Also you can compact LOH. Commented Oct 21 at 8:12

1 Answer 1

1

After lots of digging, trying all kinds of AIs, asking friends and blaming my image loading code, I finally found out that I could just set the Garbage Collector to be as agressive as possible:

GC.Collect(2, GCCollectionMode.Aggressive, true, true);

Why?

Since Images are usually somewhat big, they are being put into the Large Object Heap.

Garbage collection usually tries to leave some LOH-Memory allocated to use it as a cache for future objects (as far as I know, feel free to correct me!), but in my case, this was not necessary, and by setting the GC to agressive, I could get rid of all the wasted memory and drastically reduce RAM usage!

It took me a really long time to figure this out, and I am really glad that I found out.

If you are having simmilar problems with large piles of unused memory, try making the GC collect the different generations in agressive mode. Even if it's not images but byte arrays in general.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.