ContentControl memory leak

Oct 18, 2010 at 10:34 AM

Hi,


I think there is a memory leak somewhere, I use ActivateItem to switching between views in my application and they look like are not disposing. This happens even in the http://devlicio.us/blogs/rob_eisenberg/archive/2010/10/12/caliburn-micro-soup-to-nuts-part-6b-simple-navigation-with-conductors.aspx, if you switch between views the memory consumption will keep growing and growing.

This is not important with a simple application but I got to a point where IExplore was taking 1G of memory only by navigation between views.

I think is somehow related to the datatemplated used for the content control convetion.

I will really appreciate some feedback on this as my customers are not very happy about hitting F5 from time to time.

 

Thank you,

Oct 18, 2010 at 12:10 PM

I added an issue for this: http://caliburnmicro.codeplex.com/workitem/39
I'll investigate it shortly.

Oct 18, 2010 at 11:47 PM

I made a small repro using the sample you pointed out (which is a quite common use of the Conductor), allocating 70-100 MB on both child views and view models.
I actually had a growing memory allocation for a while, but as soon as I got some important memory pressure, the allocation dropped down to expected values.
This is a normal behavior: neither views nor view models are explicitly disposed, so they are actually kept allocated (even if no longer in use) to save CPU cycles of GC.
But as soon as free memory is requested (or you explicitly GC.Collect unused memory) all pending intances are released.

To investigate the issue in depth it's better to use a memory profiler, but unfortunately I don't have such a product readily available now.

Anyway, since I never got over 700MB even with large buffer allocation, I would also like to check if you are doing something that could possibly cause (or worsen) the issue.
Did you attempt to isolate the actual source of leaks?
A good strategy for existing application is to remove one part after another until you have the smallest possible application which still leaks.
Pay particular attention to parts with closures, lambda event handler and class with singleton lifecycle that publish events.

A small repro could greatly help to spot the problem; if you could put it together, please send it to marco dot amendola at gmail dot com

Oct 19, 2010 at 2:20 PM

Marco, can you provide a link to your repro project? I have a couple memory profilers at hand (.NET Memory Profiler and ANTS Memory Profiler) and would like to investigate this issue.

Coordinator
Oct 19, 2010 at 2:40 PM

I should also mention that there is a well know Silverlight bug that causes memory leaks when using custom DataTemplates.  It's affecting a lot of people including several of the major control vendors. The SL team is aware of it and eagerly working on a fix. Not sure if that is what the problem is here, but it could be.

Oct 19, 2010 at 2:56 PM

Hi,

Many thanks for offering to help out. After doing what Marco suggested (decomposing the application) I figured out that 

 

<telerik:GridViewDataColumn Header="Last Name" DataMemberBinding="{Binding Path=LastName}">
                            <telerik:GridViewDataColumn.CellTemplate>
                                <DataTemplate>
                                    <HyperlinkButton Content="{Binding Path=LastName}" Command="{Binding Path=Value, Source={StaticResource ViewCommand}}" />
                                </DataTemplate>
                            </telerik:GridViewDataColumn.CellTemplate>
                        </telerik:GridViewDataColumn>

this Command binding was causing the model to stick in the memory.

For the rest of the memory growth I suppose the the known datatemplate Silvelight issue is the cause, I've already opened a threat regarding this in the past http://caliburnmicro.codeplex.com/Thread/View.aspx?ThreadId=231027.

 

Thanks for all the great help,

 

Oct 19, 2010 at 4:48 PM

@BladeWise: please drop me a line on marco dot amendola at gmail dot com, I'll send you the repro back.

@Rob: I read something about that SL issue. It should be mostly related to the use inline data templates.
Since, at least in my experience, it is a *very* common scenario, I thought it couldn't have this HUGE impact on average case.
I know: once you have a leak, this could potentially prevent the release of loads of memory; yet, it's not so easy to reach 1GB...
At worst, maybe leak effects could be mitigated a little.

@Calin: Can you please send me your repro? I can see nothing wrong in your last snippet, but there may be something not so obvious in other not shown parts.
Let's say, for example, that the HLButton fails to detach from ViewCommand.CanExecuteChanged: if ViewCommand/View cluster is disposed at the same time, no damage is done.
if ViewCommand is held by some singleton, or subscribes to some global events, you pretty much have the View (and corresponding model) kept in memory forever.

 

Oct 19, 2010 at 4:56 PM

Hi Marco,

I will build a short project for you isolating the project and I will attach 2 memory profile reports showing that the instances are still keep alive even after a GC.collect

Regards,

Oct 20, 2010 at 11:20 PM

@Calin:

I played around with your repro. The leak seems to be related to the use of a Command kept into the UserControl resource dictionary and bound to a Button inside a DataTemplate, inlined into a Telerik grid.
Picking the Command from another source or using a ListBox (with an identical inline data template) almost removes the leak.

Now the code; this configuration leaks:

 

<UserControl x:Class="TestCaliburn.MyView" ...>
    <UserControl.Resources>
        <myFramework:ObservableCommand x:Key="SayHelloCommand" />
    </UserControl.Resources>
    
    <telerik:RadGridView ...>
		...
			<telerik:GridViewDataColumn.CellTemplate>
					<DataTemplate>
						<HyperlinkButton Content="{Binding Path=FirstName}"
                                         		Command="{Binding Path=Value, Source={StaticResource SayHelloCommand}}" />
					</DataTemplate>
(ViewModel and code used to configure SayHelloCommand omitted)


This alternative (and simpler!) solution doesn't leak:

 

<UserControl x:Class="TestCaliburn.MyView" ...
	xmlns:cal="http://www.caliburnproject.org">

	<telerik:RadGridView ...>
		...
			<telerik:GridViewDataColumn.CellTemplate>
				<DataTemplate>	
					<HyperlinkButton Content="{Binding Path=LastName}"
                                    			 cal:Message.Attach="SayHello" />
				</DataTemplate>

 



public MyViewModel()
{
	...
	public void SayHello()
	{
		//just a sample, don't do it at home
		MessageBox.Show("Hello");
	}
}


Even though the first version is not leveraging Caliburn.Micro messaging feature - which is BAD :) - it seems perfectly legitimate to me.
Also, the DataTemplate + Resource clues mostly resemble the Silverlight issue that Rob pointed out, maybe alredy solved.
Since there is a convenient workaround, I don't think it would be useful to investigate longer.
Let me know if you can appreciate some improvement in the full application.

@BladeWise: thank you for offering your help

Oct 21, 2010 at 3:04 PM
Edited Oct 21, 2010 at 3:05 PM

I have profiled the projects Marco sent to me (both the one produced by him, and the one produced by Calin), and I found that the issue is not related to either CM or the Telerik control.

The issue is related to Silverlight, since the cause of the leakage is a strong reference to the View (which in turn references the ViewModel) inside a static dictionary, defined into an internal Microsoft namespace (MS.Internal.ManagedPeerTable). Note that there seems to be many bug reports (like the bug related to implicit datatemplates, or the one previously cited) involving memory leakages in SL, so it is not easy to identify which bug is really the culprit without a more thorough investigation.

To utterly confirm that the leakage is not related to CM, I adapted Calin sample to WPF, using the default ListView/GridView in place of the Telerik control. As you can guess, no leakage was detected.