Reputation: 2771
I have a big solution with graphics, popups and animations. I have found out that I have a massive memory leak during page navigations.
I therefore tried with the first solution:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (App.RootFrame.CanGoBack)
App.RootFrame.RemoveBackEntry();
GC.Collect();
base.OnNavigatedTo(e);
}
This from several sources on MSDN and Stackoverflow, should remove the memory stored for the page. This was not the case and I am unsure if the MVVM structure of the code somehow keeps information stored. I then tried to implement deconstructors and force values to null when the event was fired like:
~SecondScreen()
{
In_Game_Crest = null;
currentViewModel = null;
}
This I did for all pages, popups and usercontrols. I then again went through the code using debug, and non of the pages deconstructor was ever fired. This has lead me on to try and use IDisposable
and fiddeling around with the viewmodelLocator
provided by MVVMLight, without any success.
I have read the following addressing the issue: StackOverFlow: Finalizer and Dispose
Finalize/Dispose pattern in C#
MSDN: Implementing a Dispose Method
MSDN: Implementing Finalize and Dispose to Clean Up Unmanaged Resources
But it has confused me more than it helped me. How should I implement the dispose and finalize methods for a page for my windows phone?
Since I'm using the MVVM structure should these methods be implemented in a ViewModel or behind the given page or both?
Examples for windows phones would be much appreciated.
I have read some more about the subject and found that the finalize maybe shouldn't be written? But I am still unsure. But based on this and the first MSDN link above, I tried the following:
private bool disposed = false;
public void Dispose()
{
Dispose(true);
// Take yourself off the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
currentView = null;
popup = null;
Image1 = null;
Image2 = null;
}
// Release unmanaged resources. If disposing is false,
// only the following code is executed.
this.Content = null;
// Note that this is not thread safe.
// Another thread could start disposing the object
// after the managed resources are disposed,
// but before the disposed flag is set to true.
// If thread safety is necessary, it must be
// implemented by the client.
}
disposed = true;
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~FirstPage()
{
Dispose(false);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
this.Dispose();
base.OnNavigatedFrom(e);
}
But, but but this just made my memory increase by 23MB when I got to the second screen. This leads me to the question again, how and what should I try to implement, and WHY could the memory increase?
I have seen different implementations, either using this = null
in the dispose function or using base.Dispose()
. I figure that the latter can only be used if the class is IDisposable
? Is this the way to go? If so how do I go about it?
So I used the profiler to verify that the FirstPage is not removed
From the above Figure it can be seen that the firstpage exists. In the comments I was told to look for the instances and reference to the elements. I therefore chose instances of firstpage and got:
Here it is confirmed that FirstPage is never destroyed. But I am stuck here, how should I interpret the data?
Hoping for some help.
Upvotes: 4
Views: 1183
Reputation: 2771
So what I did to solve the page memory leak was using the answer at this question:
There still exists some leak, which helped when removing the storyboards and eventhandlers, and adding them and removing them. But some memory is still there but no leak is occurring.
Upvotes: 0
Reputation: 2666
It is not actually necessary to dispose when a user navigates away from a page, the performance
implications of creating objects all over again is more than the memory load of having a page in the memory during when the application is alive.
You should take a decision between removing objects from the memory vis.a.vis recreating the same set of objects again.
Having said this you should be careful with the navigation model.
Memory problems might occur if you are creating objects every time the user is navigating to a page but not actually disposing when the user navigates away.
For this purpose I recommend fully understanding the PageBase
and NavigationHelper
or NavigationService
class in your application.
You have mentioned that FirstPage
is not removed from the memory during the life time of the app which according to me is ideal.
Place debug points in the potential places in your code where heavy objects might get created;
navigate a couple of times to different Pages, then come back.
Check the behavior then you might get a clear picture for yourself.
For all objects check that you manually invoke Dispose
.
Dispose
is a completely different concept than GarbageCollector
, Dispose
is just an contract that developers should adhere to by invoking it for releasing resources they perceive are no longer required to be maintained in the memory since garbage collection by the platform takes place at a indeterminate time.
In the sample you have posted I see that you are setting objects to null
without actually disposing. Setting to null
just changes the memory location that variable is pointing to.
It does not destroy the object immediately. an ideal dispose should be like below.
//Call on OnClosing or OnExit or similar context
protected override void Dispose(bool isDisposing)
{
if(isDisposing && !_isDisposed){
if(disposeableImage != null){
disposeableImage.Dispose();
disposeableImage = null;
}
}
}
Upvotes: 1