Threaded View

Answered: Understanding Memory consumption

Can someone please recommend a resource to help understand memory consumption in web browsers?

I have inherited a sencha touch 2.0.1 application and chrome's task manager shows the memory consumption increasing constantly while the application poll a server for data. I had thought that memory leaks would not be an issue with Javascript but having done some research it appears that this is definitely not the case.

I also noticed that if I open some tabs in the Sencha Doc's application, and then cycle through the tabs repeatedly, the memory consumption of that page will also increase and does not appear to be garbage collected. This I don't understand because no addition data should be required for a tab that has already been viewed so what is the increase in memory being used for? Doing this with eight tabs can increase the memory consumption to over 1GB quite easily and the garbage collector is not reducing this significantly. Is that a problem with chrome or the web page? Can chrome's task manager be trusted?

In my application's case it is polling a server and updating a DataView every ten seconds. The memory consumption increases steadily - overnight it increased from an in initial 40MB to almost 4GB. All that the application does is read JSON formatted data from a server and populate a DataView with it every ten seconds.

It seems to me that there are issues with memory leaks in the sencha touch/Ext framework that make it unsuitable for applications requiring constant updates of their UI. Hopefully this isn't the case!

Like in any garbage-collected environment you get memory "leaks" when unused data is kept referenced in memory "somewhere". ExtJS and ST apps are sensitive to this because they have many layers of indirection where it's possible to leak memory in this way without knowing.

The most common causes of leaks:

Having detached DOM nodes which have a circular reference with javascript variables (e.g. event listeners points to object, object property points to dom node). This is why it's very important to correctly clean up all DOM structures after using them (Ext.destroy in ExtJS, not sure what the corresponding method is in ST).

Creating closures that are kept around as event handlers, with the closure closing over variables which you no longer need in its scope (call "myvar = null" for each variable in the closure's scope which you don't need after creating the closure).

To pinpoint the cause of the leaks, you can use the chrome heap profiler, which allows you to see what's on javascript's heap. It can show delta's between snapshots so you can see where the growth in memory footprint is being spent.

I regularly use the heap profiler to go through my ExtJS app (single-page app with almost 100 K lines of code), and the ExtJS framework itself does not contain many leaks (there are some, but not many). Most of the leaks will be in application code written with assumptions about the magic of garbage collection which don't hold up in practice. I cannot say whether Sencha Touch is equally well-designed leak-wise as I have not yet profiled my ST apps.

Additionally I can provide you with these notes I made about memory leaks in ExtJS:

Typical ways in which a memory leak may occur in ExtJS:

Event listeners linking removed components to components still part of the DOM

Not properly removing components after they are not needed (e.g. not removing / destroying Ext.Window instances after they are hidden)

Closures that capture references to no-longer-needed components (e.g. a component creates a closure on another component, and accidentally has a references to "this" in the closed-over variables.)

Javascript objects on removed DOM elements (not a circular reference, but will still leak in some browsers, IE7 is the worst)

Rules of thumb to avoid leaks in ExtJS (v3, we have not upgraded to v4 yet):

When components are no longer needed, call Ext.destroy on them. Safe to call multiple times, better safe than sorry. Called automatically for nested components in the layout hierarchy. Removes all DOM references + purges event listeners.

Avoid the DOM API's (use the ExtJS layout system). When adding custom DOM elements to which you attach JS objects or event listeners, destroy them when not needed. (e.g. do clean up in "destroy" method of component that created them; override and call superclass destroy)

Use Ext.destroy() to do the clean-up on Ext.Element or Ext.Component. When adding components outside the layout hierarchy, destroy them when no longer needed. (e.g. Ext.Window with closeAction 'hide' is not destroyed when hidden; default is closeAction: "close" which calls Ext.destroy on hide)

When using "autoDestroy:false" on an Ext.Container (e.g. toolbar) be mindful of manually destroying removed elements. Components in the hierarchy are still destroyed when the container is destroyed, but not when they are removed from the container.

Careful with deferred Tasks using Ext.util.TaskRunner. Call taskrunner.stop(task) in the destroy method. Also remove all properties from inside the task object, because the "stop()" method keeps a reference to the task object around. (this is one of those framework-level leaks which you must work around)

I'm afraid there is no magic bullet, but I can tell you that this is a solvable problem which doesn't require huge engineering, just good comprehension of javascript's memory model.