Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Image loading memory leak with C#

I have a memory leak issue in my application which loads a large amount of images. I'm rather new to C#, and thought my days of memory leak issues were past. I can't figure out the problem - maybe I'm using some unmanaged modules which I'm not handle correctly?

To illustrate my problem I've simplified the core of what causes the problem and moved this to a clean project. Note that this is all silly code which doesn't reflect the original application it came from. In the test application I have 2 buttons, triggering two events.

Button 1 - Create: Setting an object to the datacontext. This will load the images and keep them alive by setting the object to the DataContext:

var imgPath = @"C:\some_fixed_path\img.jpg";
DataContext = new SillyImageLoader(imgPath);

Button 2 - CleanUp: My understanding is that if I let go of the reference holding the SillyImageLoader which again holds the images, then this will be deleted. I also explicitly trigger garbage collection just to see immediately the amount of memory after dropping the reference.

DataContext = null; 
System.GC.Collect();

When testing I'm loading a 974KB jpeg image. Holding 30 bitmap representations of this boosts the memory usage of my application from ~18MB to ~562MB. Ok. But when I hit cleanup the memory drops only to ~292MB. If I repeat Create+CleanUp I'm left with another ~250MB memory. So obviously something is still held by someone.

Here is the SillyImageLoader-code:

namespace MemoryLeakTest
{
    using System;
    using System.Drawing;
    using System.Windows;
    using System.Windows.Interop;
    using System.Windows.Media.Imaging;

    public class SillyImageLoader
    {
        private BitmapSource[] _images; 

        public SillyImageLoader(string path)
        {
            DummyLoad(path);
        }

        private void DummyLoad(string path)
        {
            const int numberOfCopies = 30;
            _images = new BitmapSource[numberOfCopies];

            for (int i = 0; i < numberOfCopies; i++)
            {
                _images[i] = LoadImage(path);
            }
        }

        private static BitmapSource LoadImage(string path)
        {
            using (var bmp = new Bitmap(path))
            {
                return Imaging.CreateBitmapSourceFromHBitmap(
                    bmp.GetHbitmap(),
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }            
        }
    }
}

Any ideas? The problem seems to be with the BitmapSource. Holding only the Bitmap there is no memory leak. I am using BitmapSource to be able to set this to the Source property of an Image. Should I do this differently? If so - I'd still like to know the answer the memory leak.

Thanks.

like image 733
stiank81 Avatar asked Nov 11 '09 12:11

stiank81


People also ask

What happens when memory leak in C?

In other words, leaks mean that dynamically-allocated memory cannot be released back to the operating system because the program no longer contains pointers that can access it. You've lost control of that piece of memory regardless of size and can no longer access it or free it.

How do you fix a memory leak?

The usual fix is to reboot. Freeing up the operating system for reuse of the space that has been taken over by memory leaks is called garbage collection. In the past, programs have had to explicitly request storage and then return it to the system when it was no longer needed.

What is memory leak in C why it should be avoided?

Memory leak occurs when programmers create a memory in heap and forget to delete it. The consequences of memory leak is that it reduces the performance of the computer by reducing the amount of available memory.


1 Answers

When you call

bmp.GetHbitmap()

a copy of the bitmap is created. You'll need to keep a reference to the pointer to that object and call

DeleteObject(...)

on it.

From here:

Remarks

You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.


You may be able to save yourself the headache (and overhead) of copying the bitmap by using BitmapImage instead of BitmapSource. This allows you to load and create in one step.

like image 197
Martin Harris Avatar answered Oct 01 '22 03:10

Martin Harris