Memory Leak?


#1

I’ve been noticing that my app’s memory usage tends to grow over time. Particularly when I jump between a lot of pages. I ran a test using the boilerplate tabs app to see if it’s inherent to Ionic or Angular. It would cycle through the pages for 3 min. This works with a plain tabs app.

var stopit = false;
var tab1 = document.querySelectorAll('.ion-home');
var tab2 = document.querySelectorAll('.ion-heart');
var tab3 = document.querySelectorAll('.ion-gear-b');
function clicky(){
  if(stopit)return;
  tab2[0].click();
  setTimeout(function(){
    tab3[0].click();
    setTimeout(function(){
      tab1[0].click();
      setTimeout(function(){
        clicky();
      },50);
    },50);
  }, 50);
}
setTimeout(function(){
  stopit = true;
}, 1000*60*3);

The result after 3 min is a DOM 200mb on Chrome and 125mb on iOS. GC seems to reclaim some of this, a larger chunk in my own app, but just a few mb of this test.

I suspect it has something to do with UI Router or scope variables hanging around longer than they should. Any ideas?


#2

There is definitely something going on there. I’m really surprised by your memory numbers though. When I start on simulator and click back and forth for about 2 minutes, I don’t get that high (about 65MB).

Do keep in mind that memory on device is generally about half of memory in simulator ( in my experience). I did the tabs app on an iPhone 5S and only got up to about 42MB.

Keep in mind that as you navigate, Ionic is tracking history and adding that to the history state. So, it can add up. At some point in an app, the history probably ought to be reset. Maybe each time “Home” is accessed? Of course that depends on UX.

Can you explain where you are putting that code to cycle between tabs? I’ve put it everywhere and can’t get the cycling to happen.


#3

In this example I was copying and pasting is directly in to the console.

Here’s a codepen that does it inside scope (sloppily).

With history tracking, I’d expect the memory to creep up slowly, but a history object/array shouldn’t take up dozens of MBs, and it doesn’t look like it does.

Looking at Chrome’s Dev Tools timeline view, you’ll notice the number of nodes and listeners increase linearly with each page transition. It would appear listeners aren’t being cleared from memory when state changes. I believe the Angular team did this on purpose to avoid unwanted GC events interrupting their animations or watch cycles. On a PC, a hundred megs or so isn’t such a big deal, so it’s a fair thing to sacrifice for top performance. On phones though…

Digging through the heap stack, it doesn’t look like anything’s terribly unusual. There’s just a lot of it. Supporting the idea that it’s disconnected but not deleted event listeners and phantom DOM nodes causing the issue, the larger and more complicated my pages get, the worse the problem seems to be. Here, I’ve added just a few more elements, and even slowed the transition speed down, but I still see an even greater memory spike.

I’ve gone ahead and created a ticket for this, though I suspect it will require a modification to UI Router itself and not Ionic.


#4

I am also experiencing a memory leak/issue. I have a bare-bones ion-side-menus style app that is a book. Like this:

<ion-side-menus>
     <ion-side-menu-content>
         <ion-nav-view name="menuContent">
             <!-- section of the book loaded into content -->
         </ion-nav-view>
    </ion-side-menu-content>
    <ion-side-menu side="left">
        <-- ul of links -->
    </ion-side-menu>
</ion-side-menus>

As I click links in the side menu, the “menuContent” nav-view gets filled with different book pages. The problem is my book pages are “heavy” - each page has 10 Mb to 20 Mb of .m4v video elements along with text. If I quickly click through the side menu to load every page, I can get the memory on my iPhone5 all the way to 500 Mb at which point Xcode force closes the app.

I’ve tried purging the $viewHistory (see code below). This code does work to purge the viewHistory, but it doesn’t solve my memory problem. Here is the code I used to purge the view history each time I swap out a section with a controller:

$rootScope.$viewHistory = {
	histories: { 
		root: { 
			historyId: 'root', 
			parentHistoryId: null, 
			stack: [], 
			cursor: -1 
		} 
	},
	backView: null,
	forwardView: null,
	currentView: null,
	disabledRegistrableTagNames: [],
	views: {}
};

#5

Same problem here. This is my scenario.

A simple app with a left-side menu with two entries.

Each one displays a list of objects stored in two arrays. This arrays are preloaded into memory once during the first load.

Navigating back and forth between this two entries (each one displays one list ob about 20 items) memory usages increases wich actually can be preaty normal. But the memory is never released even forcing garbage collection. (Red line highlights the moment GC is forced)

I tried to isolate the problem. I decided to get rid of ionic Javascript functionality. Now when I force GC memory usage drops down dramatically to 11MB wich is fine.

I am new to Ionic so I can not be as precise as I would like to be to inform about the issue which clearly looks a memory leak in Ionic.

P.S.: Got rid of Ionic angular but I still keep using angularjs and ui-router


#6

Very interesting that pure Angular GCs it.

What version of Angular did you use? I’d be curious if you see it using the same version of Angular and UI Router as Ionic does. It sounds like this has been an issue in the Angular community as well, so maybe this GC is a recent enhancement. If not, maybe there’s a certain way Ionic can do things to allow these GC events.

I’ll continue investigating as time allows.


#7

Trying to solve the problem upgraded to angular 1.3.0-beta.5. But I have just “downgraded” to 1.2.12, bundled with Ionic and GCs ok too.

Similar situation with angular ui-router. The screenshots above where taken with ui-router 0.2.10. I have downgraded to 0.2.7 which is default for ionic 1.0-beta and GCs OK

To say it briefly, GC is OK using angular and ui-routers versions included in Ionic 1.0-beta.

Seemingly, memory leak issue is morerelated to ionic I think.

Regards!


#8

I did some more investigating just now. This is on-device (iPhone5) testing and I am tracking memory in Xcode. For me, the leak is exaggerated a ton when a page has an HTML5 video element in it.

I did a simple test where I would toggle back and forth between two pages of my app. In the case where both pages just contain plain text, there is still a leak, but it is on the 50 kB - 100 kB per page swap scale, which may just be window.history.pushState() filling up the history (ui-router is probably doing this).

I did a second test where the first page contained a 12 Mb image (very large). On the initial load of the page, I see the 12 Mb memory hit, but on subsequent toggles between this page and the other with just plain text, the 12 Mb hit never shows up again - like the image is cached.

In the third test, however, where the first page contains an HTML5 video element, I now have a huge leak. Every time I revisit the page, I see the full hit of the video size on memory. With my app, which has several videos on each page, and 20 or so pages, this is an absolute killer.

[Update] I am now investigating the general issue of iOS Safari memory leaks with video and it appears this is a common problem, not particular to Ionic/Angular. http://stackoverflow.com/questions/5170398/ios-safari-memory-leak-when-loading-unloading-html5-video. The recommended solution is to set video src="" after you unload the page. I’ll try to implement this and let you know what I find.


#9

The video may be an issue, but as these tests don’t have video, it’s safe to say the video element is not the root cause. It is possible that if nodes are not being released when they should, that it would make the situation a problem more quickly when they include video.


#10

Aritzg, did your test include ng-animate? Doing some more investigation, it seems simply including ng-animate might prevent UI Router from GCing nodes properly. I’ll put together a test, but I wanted to see if you already had one.


#11

AFAIK my example is not using ng-animate at least in an explicit way. The memory leak happens both for **animation=“slide-left-right”**and animation=“no-animation”

I could not find any other animation-related attribute or directive.