Bug 42101 - [Android] ListView and Page - Memory Leaks after user navigates back from a Page
Summary: [Android] ListView and Page - Memory Leaks after user navigates back from a Page
Status: RESOLVED ANSWERED
Alias: None
Product: Forms
Classification: Xamarin
Component: Android ()
Version: 2.3.0
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-06-23 09:30 UTC by Bartłomiej Guzy
Modified: 2016-06-29 22:44 UTC (History)
6 users (show)

Tags: Android ac
Is this bug a regression?: ---
Last known good build:


Attachments
Xamar.Forms project to confirm Memory Leaks on Android (569.09 KB, application/zip)
2016-06-23 09:30 UTC, Bartłomiej Guzy
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Bartłomiej Guzy 2016-06-23 09:30:45 UTC
Created attachment 16450 [details]
Xamar.Forms project to confirm Memory Leaks on Android

---Overview---
An application written using the Xamarin.Forms suffers from severe memory leaks on Android (I haven’t verified it on other platforms yet). When user navigates to a new Page (e.g. via the PushAsync method) and then back (PopAsync) the Page object seems to stay alive forever. It can be verified both by:
- looking at the memory allocation values in the Android Device Monitor,
- simply keeping a weak reference to a Page object that should have been released- the WeakReference is going to return a valid object forever when GetTarget method is called. 
The leak is more severe if the Page contains a ListView. It seems that the whole ListView is leaked (that means the UI as well). All types of lists get leaked- those which use DataTemplateSelector and those which don’t, those which are implemented using XAML and those instantiated programmatically in C#. Assigning the null to the ItemSource property of a ListView doesn’t change anything. Assigning null to the Content property of a Page doesn’t help either.

---Test App---
I’ve created a simple test application based on the “Data Template Selector” example provided by the Xamarin team:
https://developer.xamarin.com/samples/xamarin-forms/templates/datatemplateselector/

I modified the example project the following way:
- I added the ControlPage which serves as a place from which user navigates to one of the ListPages and then back. Thanks to that I’m able to tell whether a ListPage gets garbage collected.
- I renamed the HomePage from the example project to the MultiTemplateList and modified the ViewCells by adding Images inside them so that the memory leak is easier to spot. The Images can be removed – it doesn’t change the fact that the memory leak happens.
- I added the SimpleTemplateListPage- the page that contains the ListView but it doesn’t use DataTemplateSelector. Its purpose is to verify whether a page with a simple list is going to be garbage collected after navigating back to the Control Page.
- I’ve added the PageWithoutList- it contains just Images that consume lots of memory. Its purpose is to verify whether a simple page is going to be collected after navigating back from it.
- I’ve added the MemHeavyPage- it contains images and can be added any number of times on top of the navigation stack in order to test whether GarbageCollector is going to kick in if an amount of used memory rises.
- When any of the pages described above is added to the stack (PushAsync) then the main page (ControlPage) saves a weak reference to that new page. Later on a user can click the “Send a message to zombie pages” button which is going to trigger verification of previously saved weak references. Every reference turns out to be valid (the GetTarget method always returns true and the Page object is usable) which leads to a conclusion that GC is not able to collect them.


---Dev Environment Info---
Mac: OS 10.11.5
Xamarin.Forms: v. 2.3.0.49
Xamarin Studio Community 6.0.1 (build 8)
Tested on: Samsung Galaxy Alpha Android 5.0.2, Android Emulator API 19 and 22.

---Test steps---
1. Run the Selector.Droid project provided along with this bug report.
2. The ControlPage is displayed as a first Page.
3. Open Android Device Monitor and keep an eye on memory logs.
4. Click the button “Go to the page with a list that uses Data Template Selector”. A new Page is going to be presented.
5. Swipe the list until you reach the bottom of the list. Navigate back to the ControlPage.
6. Verify the Android Device Monitor readings – the memory usage has been reduced but very slightly. Almost all the memory has been leaked.
7. Click the button “Go to the page with with single DataTemplate Cell”. A new Page is going to be presented.
8. Repeat steps 5 and 6.
9. Click the button “Go to the page with programatically created list”. A new Page is going to be presented.
10. Repeat steps 5 and 6.
11. Click the button “Go to the regular page without a list”. A new Page is going to be presented. It contains multiple Images so the memory usage rises significantly.
12. Navigate back to the ControlPage.
13. The used memory has been reduced almost to the value from before the step 11. There is a slight leak- it will be proven in the next steps that it’s the Page object that hasn’t been collected.
14. On the Control Page click “Send a message to zombie pages”. The Control Page will send a short message to every living Page (as mentioned before the Control Page holds WeakReferences to these Pages).
15. Each living Page will call Debug.WriteLine and introduce itself. Open the Application Output view in the Xamarin Studio and check the log. Every Page that has been displayed so far stayed in memory. The format of Page introduction is:
NameOfPageClass instanceNumber received Message: >>You should be bead by now<<
e.g. RegularPageWithoutList 0 received Message: >>You should be dead by now.<<
16. You can repeat previous steps in order to verify that any number of Pages can be leaked and all of them will stay in memory indefinitely.
17. Optional: on the Control Page click the “Go to HeavyPage” button. Then you can show as many instances of this Page as you wish by clicking the “Load next heavy page” button. The memory usage will rise but it won’t trigger any additional action from the Garbage Collector side that would remove old pages from points 1-16.
Comment 1 Samantha Houts [MSFT] 2016-06-24 17:29:36 UTC
*** Bug 42124 has been marked as a duplicate of this bug. ***
Comment 2 Paul Mace 2016-06-27 18:09:38 UTC
Samantha: 42124 refers to IOS, not Android.  So, unless this leak is universal PCL problem, this won't solve it.
Comment 3 Samantha Houts [MSFT] 2016-06-27 18:50:08 UTC
I apologize; I didn't see iOS mentioned anywhere on 42124.
Comment 4 Paul Mace 2016-06-27 19:23:43 UTC
42124 Forms Component IOS ;)
Comment 5 E.Z. Hart [MSFT] 2016-06-29 22:44:33 UTC
Bartłomiej,

The behavior exhibited in your example project is not instances of Page leaking; those instances will eventually be garbage collected. Garbage collection does not occur immediately after a Page is popped off of the stack; it occurs when the runtime determines it is optimal to do so (or when you force it from code).

You can force garbage collection to run so that you can see the results. In ControlPage.xaml.cs, change your ExecuteSendMessageToZombies method to the following:

private void ExecuteSendMessageToZombies (object sender, EventArgs e) 
{
    GC.Collect();
    SendMessageToListeners();
}

This will force the garbage collector to run before sending the message. Visit a couple of the pages in your example, then click the 'Send A Message' button on your control page. Keep clicking the 'Send A Message' button; as the pages become eligible for garbage collection you'll see fewer and fewer zombie messages. (Because of the way NavigationPage works, you'll always see a message from the last page you popped off the navigation stack; that page won't be eligible for garbage collection until the NavigationPage itself is ready to be collected.)