14 Nov 2012 3:32 AM #1
Answered: Understanding Memory consumption
Answered: Understanding Memory consumption
Can someone please recommend a resource to help understand memory consumption in web browsers?
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:
- 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).
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.)
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)