Am I getting obsessed with memory leaks? Give me a non-leaking browser and I'll cut it out
.
Yesterday I was hunting for a hard-to-find memory leak with r.a.d.treeview. Our component cleans up its DOM element references and DOM event handlers when it is being destroyed. Control destruction and resource disposal occurs on two occasions: when the page unloads and when the control is updated by an AJAX call (r.a.d.callback, Atlas, etc -- the mechanism is framework-agnostic). Now the treeview, being a good citizen, follows the leak prevention pattern of keeping references to DOM elements to a minimum. Most of the time it keeps an ID and gets the element, manipulates it, and releases the reference on the double. Here is a rough approximation of RadTreeView's client-side Dispose method:
============= the leaky disposal ===================
RadTreeView.prototype.Dispose = function()
{
var rootElement = document.getElementById(this.RootElementID);
//clean up rootElement event handlers here
}
==============================================
This thing works really well in a non-AJAX scenario. The treeview gets its root element and unhooks the event handlers whenever the page gets unloaded. Now, AJAX updates turn this whole thing around. AJAX frameworks do two things: update the rendered HTML on the page and execute all the client scripts, rendered by the updated controls. Here comes the surprise, by the time you get to dispose your JavaScript object, your HTML elements might not be present in the document anymore. The AJAX call could have updated the HTML already. That was the cause of the leak with the treeview -- document.getElementById(...) returned the newly replaced root element while the old one leaked. To make matters worse, there was no way to obtain a reference and unhook those events anymore.
The solution is simple. Forget the best practices and use your better judgement! All you need is to keep an additional reference to those DOM elements. Don't get them by ID and remember to release the reference when you are done:
============= the fixed version ====================
RadTreeView.prototype.AttachEvents = function()
{
this.RootElement = document.getElementById(this.RootElementID);
//attach the event handlers
}
RadTreeView.prototype.Dispose = function()
{
var rootElement = this.RootElement;
//clean up rootElement event handlers here
this.RootElement = null;
}
==============================================
Go get the tool and check if your resource disposal works with AJAX updates. Happy hunting
.