IE10 setInterval内存泄漏的解决方法 [英] Workaround for IE10 setInterval Memory Leak

查看:167
本文介绍了IE10 setInterval内存泄漏的解决方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在测试我们的Javascript库时,我认为我们在IE10(v10.0.9200.16519 - Windows 8 64位)Javascript实现 setInterval 中发现了严重的内存泄漏。 / p>

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



我们的测试用例只执行一次 setInterval 函数,然后清除间隔计时器,即一段时间后没有代码是再运行,不再可以访问任何变量(据我所知,在此代码中没有引入全局变量,除了在 onload 中运行的方法),然而这个过程需要高达半千兆字节的内存(取决于迭代次数)。



有趣的是,如果我们使用 setTimeout 方法,这不会发生(而且问题确实不是似乎存在于IE9和当前版本的Chrome,FF中。



问题可以通过这个小提琴



在Windows 8上的IE10的新实例中运行它并打开任务管理器观看内存使用情况。它将快速增长到350兆字节,并将在脚本执行后留在那里。



这是有问题的代码片段的重要部分:

  //多次调用的函数将导致IE10中的泄漏
var eatMemory = function(){
var a =空值; //捕获的闭包变量
var intervalId = setInterval(function(){
a = createBigArray(); //调用一个分配大量内存的方法
clearInterval(intervalId); //停止间隔计时器
},100);
}

(我知道很容易修复这个特定的这个封闭,并且该对象永远不会被垃圾收集。)



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



(我也在MSDN上发布了问题



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



更新 Microsoft 接受了问题并报告将在IE11中修复(预览版) - 我自己还没有证实这一点,但是(任何人?)



更新: IE 11已正式发布,我无法重现问题在那个版本上我的系统(Win 8.1 Pro 64bit)了。

解决方案

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



正如我已经写过的(和评论者的建议),这可以通过回退到 setTimeout 。这不是微不足道的,因为需要进行一些id簿记。以下是我建议的修复方法,您可以从这个小提琴中测试和分叉

  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;
返回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 。)


During testing of our Javascript library I think we found a severe memory leak in IE10's (v10.0.9200.16519 - Windows 8 64 bit) Javascript implementation of setInterval.

A simple test case showed that if a variable is captured in the closure of the function being passed as the argument for later execution it does not seem to ever become eligible for garbage collection, i.e. the browser still seems to hold a reference to the function or at least the closure variables.

Our testcase executes the setInterval function only once and then clears the interval timer, i.e. after a while no code is running anymore and no variables are accessible anymore (as far as I can see no globals are introduced in this code, except for the method to run in onload), nevertheless the process takes up half a gigabyte of memory (depending on the number of iterations).

Interestingly this does not happen if we use the setTimeout method instead (and also the problem does not seem to exist in IE9, and current versions of Chrome, FF).

The problem can be seen with this fiddle.

Run it in a fresh instance of IE10 on Windows 8 and open the task manager to watch the memory usage. It will grow quickly to 350 Megabytes and will stay there after the script was executed.

This is the important part of the problematic code piece:

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

(I know that it is easy to fix this specific piece of code. But that's not the point - this is just the tiniest piece of code we came up with that reproduces the problem. The real code actually captures this in the closure and that object is never garbage collected.)

Is there a bug in our code or is there a way to use setInterval where a closure variable holds a reference to a large object without triggering the memory leak and without reverting to "recursive" setTimeout calls?

(I also posted the question on MSDN)

Update: This issue also exists in IE10 on Windows 7, but does not exist if you switch to IE9-standards mode. I submitted this to MS Connect and will report progress.

Update: Microsoft accepted the issue and reported it to be fixed in IE11 (preview version) - I haven't confirmed this myself, yet (anybody?)

Update: IE 11 has been officially released and I cannot reproduce the problem on that version with my system (Win 8.1 Pro 64bit) anymore.

解决方案

For completeness sake I am adding a possible workaround here:

As I have already written (and commenters suggested), this can be worked around (not fixed) by falling back to setTimeout. This is not trivial, since some id book-keeping needs to be done. Here is my suggested fix, that you can 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];
    }
}

The idea is to recursively call setTimeout to simulate recurring setInterval calls. There is a little overhead in this implementation since it has to perform the bookkeeping for the changing ids, so I wouldn't recommend applying this fix unless it is required.

Unfortunately I am not able to come up with a "feature" detection algorithm (more like a "bug"-detection algorithm), so I guess you have to revert to good old browser detection. Also my implementation cannot deal with strings as the first argument and does not pass additional arguments to the inner function. Lastly it is not safe to call this method twice, so use it at your own risk (and feel free to improve it)!

(Note: For our library we will stop using setInterval from now and instead rewrite the few parts in the code that rely on it to use setTimeout directly.)

这篇关于IE10 setInterval内存泄漏的解决方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆