I have the following method in my code in my WPF
application that uses a DataGrid
:
List<IColumn> GetColumns()
{
return _columnItems
.Where(column => column is TextColumn<T> textColumn &&
textColumn.IsColumnEnabled
)
.ToList();
}
This code remains in memory even after closing a dialog containing the corresponding datagrid
. I can see it by examining dotMemory
by JetBrains
, I have a Func<IColumn, bool>
as a GC root, holding on to my DataGrid.
Yet, if I just perform this simple change:
List<IColumn> GetColumns()
{
var columnsToReturn = new List<IColumn>();
foreach (var column in _columnItems)
{
if (column is TextColumn<T> textColumn && textColumn.IsColumnEnabled)
columnsToReturn.Add(textColumn);
}
return columnsToReturn ;
}
The memory leak is gone, the instance of DataGrid
is cleaned after closing the dialog, this Func
that used to be a GC root is gone.
As much as I'm happy that I've resolved the memory leak, I would like to understand what's going on?
I assume it is related to Linq
projection and it's logic of caching entries, though I'm a bit surprised as the leaking code has ToList()
in the end, which was already supposed to execute the query. My long syntax escapes the Linq
Func (Where), and thus "cutting off" the tree branch in dotMemory
.
I mean, I already return the Func
execution from this method, as I've already invoked ToList()
, so whatever uses this method's output uses the returned list, so why would the Func
still be kept in memory by Linq
or IEnumerable
?
CodePudding user response:
Wow, this is caused due to compiler optimization.
You can find all the related info here: https://stackoverflow.com/a/46963119/3707896
In short, the compiler created a child class, put my function (lambda expression) as an instance method of that class, created a singleton instance of that class in a static field and finally created a static field with my Func referencing my method. So, no surprise that those static members generated by compiler cannot be collected by GC.
Credit to the original writer of the post in the attached link: @Evk