We have a long-running monitoring app that after about 10 minutes would begin to choke. Nothing out of the ordinary happens, we poll the server for logs every 2 seconds.
We checked our app using drip (
http://sourceforge.net/projects/ieleak) and found that the number of referenced DOM elements would grow linearly as time passed. We checked any possible sources for leaks in our code and found none (we are not using event handlers nor closures in this app, which are common sources for leaks).
The number of extra DOM elements created was directly proportional to the number of remote DWR requests we performed (that is, if we were monitoring only one log per page, we saw an increase of 1 DOM element/second, monitoring 10 logs simultaneously, it became 10 DOM elements/second). Therefore we suspected the cause was within DWR.
We narrowed it down to dwr.util.setValue(). Commenting this line in the remote callback function solved the leak, but then of course the app became useless. Replacing setValue() with an innerHTML call solved the problem, no leaks, and now we had a functioning app, but we further investigated the cause because setValue() obviously has other benefits.
The problem also went away (i.e. no leaks) when setting escapeHtml:false (we have true as the default). So we inspected util.js and found the offending code:
dwr.util.escapeHtml = function(original) {
var div = document.createElement('div');
var text = document.createTextNode(original);
div.appendChild(text);
return div.innerHTML;
}
Evidently, document.createElement() and document.createTextNode() create extra DOM elements. One would think that since both variables have local scope, they would be garbage-collected immediatly after the function call ends. However, the document seems to be hanging on to these DOM references, even if they are nowhere to be found in the document tree (at least in IE6).
This could be simply fixed by implementing a proper escaping function, instead of relying on DOM manipulation to perform the same task. It is extremely simple, and the following code could be used:
dwr.util.escapeHtml = function(original) {
if (original == null) return null;
if (original.indexOf('&') == -1 &&
original.indexOf('<') == -1 &&
original.indexOf('>') == -1 &&
original.indexOf('\"') == -1 &&
original.indexOf('\'') == -1) {
return original;
}
var length = original.length;
var output = '';
for (var i = 0; i < length; i++) {
var c = original.charAt(i);
switch (c) {
case '&':
output += '&';
break;
case '<':
output += '<';
break;
case '>':
output += '>';
break;
case '\"':
output += '"';
break;
case '\'':
output += ''';
break;
default:
output += c;
break;
}
}
return output;
}
I've attached drip.exe