javascript – IE10 setInterval内存泄漏的解决方法

前端之家收集整理的这篇文章主要介绍了javascript – IE10 setInterval内存泄漏的解决方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在我们的 Javascript库测试期间,我认为我们发现IE10的(Win10.0.9200.16519 – Windows 8 64位)Javascript实现的setInterval存在严重的内存泄漏.

一个简单的测试用例表明,如果一个变量被捕获在被传递的函数的闭包中,作为稍后执行的参数,似乎没有资格进行垃圾收集,即浏览器似乎仍然存在对该函数的引用,至少关闭变量.

我们的测试用例只执行一次setInterval函数,然后清除间隔定时器,即一段时间后没有任何代码正在运行,并且没有任何变量可以被访问(我可以看到,在这段代码中没有引入全局变量,除了方法运行在onload),但是这个过程占用了半GB的内存(取决于迭代次数).

有趣的是,如果我们使用setTimeout方法(而且IE9中的问题似乎不存在,Chrome,FF的当前版本)也不会发生这种情况.

this fiddle可以看出问题.

在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);
}

(我知道很容易修复这个特定的代码段,但这不是重点 – 这只是我们提出的最小的代码,它重现了这个问题,真正的代码实际上是在封闭和对象中捕捉到的从来没有收集垃圾.)

在我们的代码中是否有错误,或者有没有办法使用setInterval,其中一个闭包变量保存对大对象的引用,而不会触发内存泄漏,而不会恢复为“递归”setTimeout调用

(我也是posted the question on MSDN)

更新:Windows 7中的IE10中也存在此问题,但如果切换到IE9标准模式,则不存在.我提交给MS Connect并报告进度.

更新:Microsoft accepted the issue并报告它在IE11(预览版)中修复 – 我还没有确认这个,但(任何人?)

更新:IE 11已正式发布,我无法使用我的系统(Win 8.1 Pro 64bit)再现该版本的问题.

解决方法

为了完整起见,我在这里添加了一个可能的解决方法

正如我已经写的(和评论者建议),这可以通过回退到setTimeout来解决(不固定).这不是微不足道的,因为需要完成一些id记录.这是我建议的修复,你可以test and fork from this fiddle

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”检测算法),所以我想你必须恢复到旧的浏览器检测.此外,我的实现不能将字符串作为第一个参数来处理,也不会将额外的参数传递给内部函数.最后两次称这个方法是不安全的,所以使用它自己的风险(并随意改进它)!

(注意:对于我们的库,我们将从现在开始停止使用setInterval,而是重写依赖它的代码中的几个部分直接使用setTimeout.)

猜你在找的JavaScript相关文章