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?