在测试我们的Javascript库时,我认为我们在IE10的Javascript实现(v10.0.9200.16519-Windows864位)中发现了严重的内存泄漏。
一个简单的测试用例表明,如果在传递的函数的闭包中捕获了一个变量,作为以后执行的参数,它似乎就没有资格进行垃圾收集,也就是说,浏览器似乎仍然保存对函数的引用,或者至少保存闭包变量。
我们的测试用例只执行setInterval函数一次,然后清除间隔定时器,即过了一段时间后,不再运行代码,也不再可以访问变量(据我所见,除了在onload中运行的方法之外,在此代码中没有引入全局值),但是进程占用了半on的内存(取决于迭代的次数)。
有趣的是,如果我们转而使用setTimeout方法,这种情况就不会发生(而且在IE9和当前版本的Chrome中似乎不存在这个问题)。
从这把小提琴中可以看出这个问题。
在Windows 8上一个新的IE10实例中运行它,并打开任务管理器来查看内存的使用情况。它将迅速增长到350兆字节,并将在脚本执行后留在那里。
这是有问题的代码片段的重要部分:
// the function that when called multiple times will cause the leak in IE10
var eatMemory = function() {
var a = null; // the captured closure variable
var intervalId = setInterval(function() {
a = createBigArray(); // call a method that allocates a lot of memory
clearInterval(intervalId); // stop the interval timer
}, 100);
}(我知道修复这段特定的代码很容易。但这不是重点--这只是我们想出的最微小的一段代码,它再现了问题。真正的代码实际上捕获了闭包中的this,并且该对象从未被垃圾收集。)
我们的代码中是否有bug,或者是否有一种使用setInterval的方法,其中闭包变量保存对大型对象的引用,而不触发内存泄漏,并且不恢复到“递归”setTimeout调用?
(我也是在MSDN上发布这个问题)
更新:在Windows 7上的IE10中也存在此问题,但如果切换到IE9标准模式,则不存在此问题。我将此提交给Connect女士,并将报告进展情况。
更新: Microsoft 接受了这个问题并报告说它在IE11 (预览版)中被修复--我自己还没有确认(有人吗?)
更新: IE 11已经正式发布,我不能再用我的系统(W温8.1 Pro 64位)在那个版本上重现问题了。
发布于 2013-04-10 07:22:42
为了完整起见,我在这里添加了一个可能的解决方法:
正如我已经写过的(和评论者建议的),这可以通过回到setTimeout来解决(而不是固定的)。这并不是小事一桩,因为需要做一些身份记账工作。这是我建议的修补程序,您可以测试和叉子从这把小提琴
var registerSetIntervalFix = function(){
var _setTimeout = window.setTimeout;
var _clearTimeout = window.clearTimeout;
window.setInterval = function(fn, interval){
var recurse = function(){
var newId = _setTimeout(recurse, interval);
window.setInterval.mapping[returnValue] = newId;
fn();
}
var id = _setTimeout(recurse, interval);
var returnValue = id;
while (window.setInterval.mapping[returnValue]){
returnValue++;
}
window.setInterval.mapping[returnValue] = id;
return returnValue;
}
window.setInterval.mapping = {};
window.clearInterval = function(id){
var realId = window.setInterval.mapping[id];
_clearTimeout(realId);
delete window.setInterval.mapping[id];
}
}其思想是递归调用setTimeout以模拟反复出现的setInterval调用。这个实现中有一些开销,因为它必须为不断变化的id执行簿记操作,所以我不建议应用此修复,除非是必需的。
不幸的是,我无法提出一个“特征”检测算法(更像是一个"bug"-detection算法),所以我想您必须恢复到好的老浏览器检测。而且,我的实现不能将字符串作为第一个参数来处理,也不会将附加的参数传递给内部函数。最后,两次调用此方法是不安全的,因此使用它需要您自己的风险(并随时改进它)!
(注意:对于我们的库,我们将从现在起停止使用setInterval,而是重写代码中依赖它直接使用setTimeout的几个部分。)
https://stackoverflow.com/questions/15905652
复制相似问题