Memory Leak in WPF ListBox with ListBoxItems in Navigation Windows
Description
When using a ListBox control with ListBoxItem in a WPF Navigation Window, opening and closing the window multiple times leads to memory leaks. The ListBox and its related objects are not released from memory, as confirmed using the .NET Memory Profiler.
Reproduction Steps
- Create a WPF Navigation Window with a ListBox containing ListBoxItems.
- Open the navigation window multiple times and close it.
- Use a memory profiler to observe memory allocations.
- Notice that ListBox, ListBoxItem, and other related objects like Event from ItemContainerGenerator and ItemCollection remain in memory.
Refer the attached sample to replicate the issue. TextBoxExtSample.zip
Expected behavior
When the Navigation Window is closed, the ListBox and its child elements should be released, and garbage collection should reclaim the memory.
Actual behavior
The ListBox, ListBoxItem, and related objects like Event from ItemContainerGenerator and ItemCollection remain in memory, causing an accumulation of memory usage over time, leading to a memory leak.
Regression?
Not sure if this issue was present in previous versions of WPF.
Known Workarounds
No response
Impact
The memory leak leads to increased memory usage in applications that frequently open and close Navigation Windows, eventually causing performance degradation.
Configuration
No response
Other information
Attached the reference image
I'd say this just a misunderstanding on how Garbage collection works. I've tested your sample briefly.
Instead of pressing a button; go big, add a DispatcherTimer to open the window each 400 ms for example; add this to your Window1 so you can see it was actually fully loaded for the full graphical image.
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
await Task.Delay(200); // cause eager event exit
this.Close();
}
And after you've opened and closed the window 10k times for example (up to you how long you want to wait), force the collection via snapshot, measure the objects and heap usage (or search for the mentioned ListBoxItem directly for example); you'll see you're back to the amount as when you've opened the window the first time.
See https://github.com/dotnet/wpf/issues/9827#issuecomment-2429829612 for example.
@Kamalesh-Periyasamy are we good to close this issue based on the comment by @h3xds1nz?
@himgoyalmicro , I will check and acknowledge here.
Hi @h3xds1nz , @himgoyalmicro
Thank you for your response. I have tested the suggested steps, followed the instructions provided, and performed 100 navigations while monitoring the profiling results. I observed that one object remained alive for the ListBox. I would appreciate it if you could provide further guidance or suggest an alternative solution to completely clear all ListBox objects and prevent memory leaks.
In our requirement, we need only a few navigations, and after calling GC.Collect, all objects were removed except for one. I have attached the tested sample and the profiling image for your reference.
Thank you for your time and assistance.
Hi Team, Is there any updates regarding the memory leak issue?
@Kamalesh-Periyasamy You're holding a strong reference to the last Window1 in MainWindow.xaml.cs in the window1 variable.
How do you expect the GC to clean that one up? Either set it to NULL after you stop the timer or use a WeakReference<T>.
So that being said, that memory leak is on you, not the framework.
@h3xds1nz @himgoyalmicro I have replaced the strong reference to Window1 with a WeakReference<Window1> and also set the original reference to null after stopping the timer. Using WeakReference appears to resolve the issue in this scenario, as no memory leak is observed during testing.
However, the memory leak still persists in a different scenario. I am currently using a Framework TabControl, where the ListBox is defined inside a UserControl. This UserControl is added to the TabItem content through a button click. Even after removing the tab items via another button click, the memory profiler indicates that the ListBox and its list items are not being released.
We have updated the sample project and included a profiler screenshot for your reference. We would greatly appreciate it if you could review the updated sample and share any suggestions or guidance on how to resolve the issue.
@Kamalesh-Periyasamy You're still failing to understand how GC works. Forcing a collection via GC.Collect does not mean that all hanging objects will be cleared on that pass. It is not an equivalent to calling "Free". To be precise, you're clearing out the collection within the same scope you're calling Collect, where you are also iterating. Poor GC having to track the refs there.
On that note, I do not see an issue when I force another GC few seconds later since there's simply no memory pressure: