As I have mentioned earlier (see "To leak, or not to leak…(memory) – Part One") the closures are the most common reason for memory leaks in Internet Explorer.
They are extremely good at forming circular references especially in the context of Host objects. The most common example is event handlers “owned” by a Native object which are attached to a specific event raised by a DOM element and hold a reference to that DOM object kept in the Native object.
DOMElement.Event -> NativeObject.EventHandler -> current execution context scope ->
NativeObject. DOMElementRefference -> DOMElement
Very frequently websites and web applications with a rich UI include components which produce such types of references and closures may consume most of the free memory. That’s why it’s very important to do some profiling and see how much third-party components “leak” before deciding to go with a given solution. Many of our products had severe leaking problems but for quite some time this issue has been one of our top priorities and we have seriously optimized the r.a.d.controls.
The other issue that comes as a side effect of closures is that Internet Explorer “gets slower” and that has nothing to do with the lack of free memory. In fact there are no clear reasons why this happens - as far as we have researched the possible reason may be that a lot of “dead” entries from closures and circular references are left in the reference lists, and with the time their traversal consumes more and more time. Quick note: don’t take this as a revelation as we don’t have access to “core” technical information regarding IE and we can’t be sure we are correct in our assumptions:)
A good example on closures could be found on Scott's "SiteExperts" Place (http://spaces.msn.com/members/siteexperts/Blog/cns!1pNcL8JwTfkkjv4gg6LkVCpw!338.entry
Here is a simple closure (I’m using the code from his post as I am a lazy guy) that is not garbage collected due to the lack of references in the given execution context (more on execution contexts can be found at http://jibbering.com/faq/faq_notes/closures.html#clExCon)
var el = document.createElement("div");
This small function creates a new execution context and scope due to the internally declared DoThis function and thus creates a circular reference that will leak memory in Internet Explorer. The best solution (the one that we prefer using in our components) is to forcefully dispose the objects that are created. Here is the modified example:
var el = document.createElement("div");
el = null;
We dispose/detach all the event handlers on the unload event.
With the risk of sharing some proprietary knowledge about our r.a.d.menu component, I would like to shed some light on how we are handling the issue – our approach is as follows (only to mention that exactly the same and even approaches are implemented for r.a.d.grid and r.a.d.editor):
1. Using advanced browser “sniffing” at the client we detect whether the running browser is IE and the platform is Windows. If so, we attach an event handler to the unload event of the window (window.onunload)
That gets executed exactly when we must fix the generated memory leaks.
2. In the attached event handler the following processing occurs:
- the original onunload handler is executed (if any);
- then a disposal function traverses all menus found on a page, clearing all the attached event handlers and any references to DOM or Host objects known of. Also some expando properties of the Host objects are cleared like the IFRAME shims generated by the menu component (if any)
The sad thing is that there are also “bugs” in IE that do not follow any of the described patterns like the createElement bug. When certain elements are created in the DOM structure, like DIVs, we get a memory leak. Most certainly this behavior is once again caused by the “Host to Native” objects difference, their scope, and the fact that they have very different implementation as a technology. More on this particular topic will be discussed in future posts on leaks and how to deal with them. The posts will come from the “leak” gurus from the r.a.d.editor and r.a.d.grid teams who have dealt with some of the nastiest problems the telerik team has encountered.
So to recap, before you forget the most important things in this rather long post:
1. be careful when you write client-side code and avoid circular references with closures
2. use functions for disposal
3. rest assured that we are doing our best trying to limit leaks in our products. It’s very tough to overcome faulty and non-optimal behavior in some modern browsers but we have made a lot of progress over the last year and we won’t stop until we make the r.a.d.controls THE MOST optimized set of controls on the market
4. enjoy the Holidays and forget about the tech stuff for a while
Best wishes for 2006 on behalf of everyone on the telerik team!
As Chief Innovation Officer at Progress, Vassil Terziev is responsible for identifying growth strategies and new market opportunities, as well as promoting internal innovation.