FilteredBindingList Memory Leak

FilteredBindingList Memory Leak

Old forum URL: forums.lhotka.net/forums/t/6857.aspx


tstout posted on Monday, April 27, 2009

I have encountered a situation where FilteredBindingList is causing a memory leak. FilteredBindingList adds an event listener to the source list in the constructor, but does not provide a mechanism to remove the listener. I realize that LINQ makes FilteredBindingList obsolete, but what is a good strategy to deal with this problem in an environment where LINQ is not available? I experimented with modifying FilteredBindingList such that it implements IDispose to remove the listener and added 'using' as appropriate and this corrected the leak, but I'd rather not create a one-off version of CSLA to work around this problem.

RockfordLhotka replied on Monday, April 27, 2009

Yes, I suppose in a case where you are filtering a cached list, or some other source list that doesn't go away this would be an issue.

In a great many cases this is a non-issue, because the source list goes away when the filtered list does, because they are all tied to a UI form or something along that line.

Nonetheless, for the case where the source list is cached, it is worth doing as you suggest. I've added an item in the issue tracker

http://www.lhotka.net/cslabugs/edit_bug.aspx?id=394

tstout replied on Monday, April 27, 2009

Thanks for the definitive answer. I must admit that my use case is a little unusual. I'm deploying to .Net 3.5 (a recent luxury) so I simply modified the cached lists to be created via LINQ instead of FilteredBindingList. Are there any caveats regarding data binding to be aware of when using a collection generated via LINQ instead of Filtered/SortedBindingList?

amselem replied on Friday, June 19, 2009

I think that this issue also applies to LinqBindingList. LinqBindingList also adds an event listener to the source list and doesn't ever remove it, so I have a situation where I'm having memory leaks:

I have a BLB with ~40 items wich can be recalculated as many times as the user wants before saving. The list gets cleared and each item is recalculated using values from other previous calculated items from the same BLB. I'm doing the lookup of previously added items using a LINQ expression wich is creating an underlying LinqBindingList. I debugged that the LBL is handling the ListChangedEventHandler event multiple times for each item I added, and it gets worse as the number of recalculations increases.

Maybe I'm in an edge case, but I think that LinqBindingList should implements IDisposable as tstout suggested, what do you think?

Regards.

RockfordLhotka replied on Saturday, June 20, 2009

The same issue likely applies. LINQ to CSLA often creates a LinqBindingList, which is a view over the original list, much like FilteredBindingList. It doesn't implement IDisposable either.

The thing with implementing IDisposable in any of these view classes, is that YOU will have to call Dispose(). And normal coding practices when people use these objects don't allow the using statement, because data binding is involved. So while we might add IDisposable at some point, it will not be a miracle solution - it'll just allow you to do a bunch of work in your UI to avoid the memory issue that started this thread.

amselem replied on Saturday, June 20, 2009

I understand the case with FilteredBindingList because is used directly in the code.
But in this case I was not using LinqBindingList directly, I only writed a Linq expression, and the framework creates an underlying LinqBL... so, it isn't possible for the fw to automatically dispose after using it?
Even if LinqBL implemented the IDisposable interface I don't know how to invoke it.

Regards

RockfordLhotka replied on Saturday, June 20, 2009

That is my point exactly - you would need to change your code to hold a
reference to the result of the L2C query, check to see if it implements
IDisposable, and call Dispose() manually.

Remember that .NET has no reference counting, so there's no deterministic
way to know when an object is no longer in use. And even if there was
reference counting it wouldn't help, because the event handler causes the
event source (the original list) to set up a reference to the LBL (or FBL).

It is really not at all clear to me that this is a problem we can solve - at
least not in a way that is actually practical for anyone to use.

Rocky


-----Original Message-----
From: amselem [mailto:cslanet@lhotka.net]
Sent: Saturday, June 20, 2009 2:00 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] LinqBindingList Memory Leaks

I understand the case with FilteredBindingList because is used directly in
the code.
But in this case I was not using LinqBindingList directly, I only writed a
Linq expression, and the framework creates an underlying LinqBL... so, it
isn't possible for the fw to automatically dispose after using it?
Even if LinqBL implemented the IDisposable interface I don't know how to
invoke it.

Regards

oscarmorasu replied on Thursday, February 04, 2010

We ran into the same problem using LINQ queries on our BLBs.
Our app only runs locally in the PC, so we load our business objects from disk and we keep them in memory until the app shuts down.
That means every LinqBL created from any BLB will remain in memory.
Our workaround was removing the code where the LinqBL subscribes to the BLB.ListChanged event.
The obvious consequence is the LinqBL won't be notified when the BLB changes, but's that fine for our implementation.
Is there any other unintended issue that could arise by doing this?
Do you have any plans to fix this in a future release?
Thanks in advance.

RockfordLhotka replied on Thursday, February 04, 2010

Yes, in CSLA 4 creating a LBL will be an explicit operation.

In 3.8 you get an LBL unless you tweak your query to prevent it. But in 4 you'll have to do an explicit .ToSyncList() operation (or something like that) to get an LBL. So by default you'll get the standard IEnumerable<T> returned by LINQ.

oscarmorasu replied on Thursday, February 04, 2010

Thanks for the quick answer Rocky,
we'll keep our eyes open for the version 4 release.

Copyright (c) Marimer LLC