0

I am building a Photo Editor application with WPF and using WriteableBitmap as the source of the main image since I can change its content. One issue I am dealing with is a memory leak when changing the image. The application is large and complex, but a small example here shows the issue:

BitmapSource bitmapSource;
WriteableBitmap writeableBitmap;

private BitmapSource LoadBitmapSource(string path)
{
    BitmapImage bitmap = new BitmapImage();
    bitmap.BeginInit();
    bitmap.CacheOption = BitmapCacheOption.None;
    bitmap.UriSource = new Uri(path, UriKind.RelativeOrAbsolute);
    bitmap.EndInit();
    return bitmap;
}

private void Load_Click1(object sender, RoutedEventArgs e)
{
    int w = 7356, h = 4864;
    var p = PixelFormats.Bgr24;

    writeableBitmap = new WriteableBitmap(w, h, 96, 96, p, null);
    writeableBitmap.Freeze();
    img_Image.Source = writeableBitmap;
}

private void Load_Click2(object sender, RoutedEventArgs e)
{
    bitmapSource = LoadBitmapSource("test_image.jpg");
    bitmapSource.Freeze();
    img_Image.Source = bitmapSource;
}

private void Remove_Click(object sender, RoutedEventArgs e)
{
    bitmapSource = null;
    writeableBitmap = null;
    img_Image.Source = null;
    img_Image.UpdateLayout();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
}

It is a simple interface with 1 Image and 2 Buttons (Load and Remove). For load we have 2 examples, the first will create a WriteableBitmap and the second uses a file image as the source for a BitmapImage. Both tests are done on Release to get better performance and use Task Manager to monitor memory usage. Both tests are done on similar image properties; 7356x4864, Bgr24 image.

WriteableBitmap

With the WriteableBitmap (Load_Click1) we do start with 16MB of memory, and when the load button is clicked, the memory usage jump to 390MB after the image is shown the GC did its work. When the remove button is clicked, the memory usage goes down to 152MB, and it is supposed to go to its start memory usage point or at least close to it, a 136MB memory still allocated is a big problem. It stays the same, even after clicking the remove button multiple times.

BitmapImage

With the BitmapImage (Load_Click2) we do start with 16MB of memory as before, and when the load button is clicked, the memory usage jump to 288MB. When the remove button is clicked, the memory usage goes down to 152MB. The result would be the same if we use BitmapCacheOption.None or BitmapCacheOption.OnLoad.

The issue seems to be related to Image control itself. If we didn't put the BitmapImage/WriteableBitmap as the source of the Image control and just kept it assigned to its variable, the memory returns to its start point.

I am not sure if this is normal behavior or not, it could be that WPF is caching something for better performance in the long run, especially since clicking Load->Remove multiple times does not result in OutOfMemoryException, in fact, the memory usage stays around 150MB. But it's still an issue if you load a large image, then a small one and the application will keep using a lot of memory.

Can someone please point out what I am doing wrong here, and offer an explanation of why that memory is still allocated even after the objects should be handled by GC?

Mattis
  • 1
  • 1
  • I would be far more concerned about a steady, systemic rise in memory usage as processing continues. Is there any way you can automate this to make it happen 50 times? 100 times and profile that? Certainly there is going to be some overhead the first time you use something. – Joe Oct 03 '22 at 17:25
  • @Joe I'vn't automate it, just to not use multi-threading and it is very important to let the UI present the image first before removing it. I've tested that manually, and the result as stated before, the memory usage stays around 150-160 MB even if I have done it 100 times. I just wanna understand why that memory is still allocated even when using all options to prevent bitmap from caching data. – Mattis Oct 03 '22 at 17:43
  • Well I will admit I don't know for certain but my theory would be that System.Windows.Media would cache things that are not necessarily bitmap data. But set my speculation aside, have you tried using the Visual Studio Diagnostic tools and taking a memory snapshot both before and after the allocation? – Joe Oct 03 '22 at 18:20
  • It's all right @Joe, it is not easy to know something that happens underneath. I've tried VS memory profiler and DotMemory and they don't provide much help. That memory is unmanaged, so they will not give you much information about it, like which object owns it or why it is used for. – Mattis Oct 03 '22 at 18:30
  • I am in a similar boat. My software streams 5 MP images (sometime 25 MP) live from cameras, displaying them live in WPF, saving them at the press of a button. Other displays statically show saved images, switch images at the press of a button, etc. I'm fighting this too. And yeah the memory tools aren't much help – Joe Oct 03 '22 at 18:47
  • Yes, there are a lot of issues with this, it would've been better if they just made Bitmap object just like System.Drawing one, where you could simply dispose of it when you don't want it, but that is easy to do because of how different parts of WPF works, and obviously, their approach has a lot of benefits. – Mattis Oct 04 '22 at 11:42

0 Answers0