I have an application(WPF) which creates BitmapImages in huge numbers(like 25000). Seems like framework uses some internal logic so after creation there are approx 300 mb of memory consumed(150 virtual and 150 physical). These BitmapImages are added into Image object and they are added into Canvas. The problem is that when I release all those images memory isn't freed. How can I free memory back?
The application is simple: Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Canvas x:Name="canvas" Grid.ColumnSpan="2"></Canvas>
<Button Content="Add" Grid.Row="1" Click="Button_Click"/>
<Button Content="Remove" Grid.Row="1" Grid.Column="1" Click="Remove_click"/>
</Grid>
Code-behind
const int size = 25000;
BitmapImage[] bimages = new BitmapImage[size];
private void Button_Click(object sender, RoutedEventArgs e)
{
var paths = Directory.GetFiles(@"C:\Images", "*.jpg");
for (int i = 0; i < size; i++)
{
bimages[i] = new BitmapImage(new Uri(paths[i % paths.Length]));
var image = new Image();
image.Source = bimages[i];
canvas.Children.Add(image);
Canvas.SetLeft(image, i*10);
Canvas.SetTop(image, i * 10);
}
}
private void Remove_click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < size; i++)
{
bimages[i] = null;
}
canvas.Children.Clear();
bimages = null;
GC.Collect();
GC.Collect();
GC.Collect();
}
This is a screenshot of ResourceManager after adding images
There was a bug in Wpf that we were bitten by where BitmapImage objects are not released unless you freeze them. https://www.jawahar.tech/home/finding-memory-leaks-in-wpf-based-applications was the original page where we discovered the issue. It should have been fixed in Wpf 3.5 sp1 but we were still seeing it in some situations. Try changing your code like this to see if that is the problem:
bimages[i] = new BitmapImage(new Uri(paths[i % paths.Length]));
bimages[i].Freeze();
We routinely freeze our BitmapImage objects now as we were seeing other instances in the profiler where Wpf was listening for events on the BitmapImage and thereby keeping the image alive.
If the Feeze() call isn't an obvious fix for your code, I would highly recommend using a profiler such as the RedGate Memory Profiler - that will trace a dependency tree that will show you what it is that is keeping your Image objects in memory.
What worked for me was to:
My cleanup method for each Image ended up being as simple as this:
img.Source = null;
UpdateLayout();
I was able to arrive at this through experimentation by keeping a list with a WeakReference() object pointing at every BitmapImage that I created and then checking the IsAlive field on the WeakReferences after they were supposed to be cleaned up in order to confirm that they'd actually been cleaned up.
So, my BitmapImage creation method looks like this:
var bi = new BitmapImage();
using (var fs = new FileStream(pic, FileMode.Open))
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = fs;
bi.EndInit();
}
bi.Freeze();
weakreflist.Add(new WeakReference(bi));
return bi;
I followed the answer given by AAAA. Orignal code causing memory filled up is:
if (overlay != null) overlay.Dispose();
overlay = new Bitmap(backDrop);
Graphics g = Graphics.FromImage(overlay);
Inserted AAAA's code block, C# add "using System.Threading;" and VB add "Imports System.Threading":
if (overlay != null) overlay.Dispose();
//--------------------------------------------- code given by AAAA
Thread t = new Thread(new ThreadStart(delegate
{
Thread.Sleep(500);
GC.Collect();
}));
t.Start();
//-------------------------------------------- \code given by AAAA
overlay = new Bitmap(backDrop);
Graphics g = Graphics.FromImage(overlay);
Repeat looping this block now makes a steady and low memory footprint. This code worked using Visual Studio 2015 Community.
This is still an array
BitmapImage[] bimages = new BitmapImage[size];
Arrays are continuous fixed-length data structures, once memory allocated for the whole array you cannot reclaim parts of it. Try using another data structures (like LinkedList<T>
) or other more appropriate in your case
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With