-->

是否JavaScript的setInterval()方法导致内存泄漏?(Does JavaScrip

2019-07-17 14:23发布

目前正在开发一个基于JavaScript的动画项目。

我注意到,正确使用setInterval() setTimeout()甚至requestAnimationFrame分配内存,没有我的请求,并导致频繁的垃圾收集调用。 更多GC来电=闪烁:-(

例如; 当我通过调用谷歌浏览器的init()执行以下简单的代码 ,内存分配+垃圾收集精为先20-30秒...

function init()
{
    var ref = window.setInterval(function() { draw(); }, 50);
}

function draw()
{
    return true
}

不知怎的,一分钟之内左右,开始在分配的内存一个陌生的增加! 由于的init()被调用仅一次, 究竟是什么在分配的内存大小增加的原因?

(编辑:铬截图上传)

注1:是的,我已经试了下setInterval的调用之前clearInterval()()。 问题是一样的!

注2:为了找出问题,我让上面的代码简单和愚蠢。

Answer 1:

编辑: 尤里的回答是好。


TL;博士IMO也没有内存泄漏。 正斜率是简单的setInterval和setTimeout的的效果。 垃圾被收集,通过锯齿图案作为看到的,根据定义这意味着没有内存泄漏。 (我认为)。

我不知道有一种方法来解决这个所谓的“内存泄漏。” 在这种情况下,“内存泄漏”指的是每个调用setInterval函数增加的存储器使用量,通过在存储器中的分析器正斜率所看到。

现实情况是,没有实际的内存泄漏:垃圾收集器仍然能够收集记忆。 通过定义存储器泄漏“时的计算机程序获取存储器,但无法将其释放回操作系统发生”。

如图所示由以下存储器型材,内存泄漏没有发生。 内存使用量与每个函数调用增加。 该OP预计,因为这是同一个函数被调用一遍又一遍,应该没有内存增加。 然而,这种情况并非如此。 内存消耗与每个函数调用。 最终,垃圾被收集,产生锯齿图案。

我摸索重新排列间隔的几种方式,他们都导致相同的锯齿形(虽然一些尝试导致垃圾收集永远不会发生的事情作为参考被保留)。

function doIt() {
    console.log("hai")
}

function a() {
    doIt();
    setTimeout(b, 50);
}
function b() {
    doIt();
    setTimeout(a, 50);
}

a();

http://fiddle.jshell.net/QNRSK/14/

function b() {
    var a = setInterval(function() {
        console.log("Hello");
        clearInterval(a);
        b();                
    }, 50);
}
b();

http://fiddle.jshell.net/QNRSK/17/

function init()
{
    var ref = window.setInterval(function() { draw(); }, 50);
}
function draw()
{
    console.log('Hello');
}
init();

http://fiddle.jshell.net/QNRSK/20/

function init()
{
    window.ref = window.setInterval(function() { draw(); }, 50);
}
function draw()
{
    console.log('Hello');
    clearInterval(window.ref);
    init();
}
init();​

http://fiddle.jshell.net/QNRSK/21/

显然setTimeoutsetInterval是没有正式的Javascript部分(因此他们不是V8的一部分)。 实施是留给实施者。 我建议你看一看的setInterval的执行情况,这样在node.js中



Answer 2:

这里的问题是不是在代码本身,它不泄漏。 正是由于时间轴面板的实现方式。 当时间轴记录事件,我们收集的setInterval回调每次调用JavaScript的堆栈跟踪。 堆栈跟踪在JS堆第一分配,然后复制到本地数据结构,堆栈跟踪复制到它变得在JS堆垃圾天然事件之后。 这反映在图形上。 禁用以下呼叫http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/TimelineRecordFactory.cpp#L55使得存储器图形平面。

有关于这个问题的错误: https://code.google.com/p/chromium/issues/detail?id=120186



Answer 3:

每次进行函数调用时,它会创建一个堆栈帧 。 不像许多其他语言,Java脚本存储在堆栈帧,就像一切。 这意味着每次调用的函数,你正在做的每50ms的时间,一个新的堆栈帧被添加到堆。 这就增加了,并最终垃圾收集。

它有点不可避免的,给了JavaScript是如何工作的。 能够真正做到以减轻它的唯一的事情就是让尽可能小的堆栈帧,这一点我敢肯定,所有的实现做。



Answer 4:

我想你大概的setInterval和闪烁的评论作出回应:

我注意到,正确使用的setInterval(),setTimeout的(),甚至requestAnimationFrame的内存分配没有我的请求,并导致频繁的垃圾收集调用。 更多GC来电=闪烁:-(

你可能想尝试使用基于setTimeout的少邪自调用函数替换的setInterval调用。 保罗爱尔兰提到这在谈话称为10件事情我从jQuery源教训(视频这里 ,注意到这里看到#2)。 你要做的就是与间接调用自身通过的setTimeout 它完成它应该做的工作后,一个函数替换您对setInterval调用。 引述谈话:

许多人认为,setInterval的是一个邪恶的功能。 它使停靠在指定的时间间隔的功能,无论该功能是否结束与否。

使用上面的示例代码,你可以从更新您的初始化函数:

function init() 
{
    var ref = window.setInterval(function() { draw(); }, 50);
}

至:

function init()
{
     //init stuff

     //awesome code

     //start rendering
     drawLoop();
}

function drawLoop()
{
   //do work
   draw();

   //queue more work
   setTimeout(drawLoop, 50);
}

这将有助于一点,因为:

  1. 画()将不会直到它完成再由你的渲染循环中调用
  2. 因为许多上述答案指出,所有的不间断的功能从setInterval的调用都放在头顶上的浏览器。
  3. 调试有点容易,因为你不通过的setInterval不断发射中断

希望这可以帮助!



Answer 5:

铬是很难从看到你的程序的任何内存压力(1.23 MB是用今天的标准非常低内存占用),所以它可能不认为它需要积极GC。 如果修改方案,以使用更多的内存,你会看到在垃圾收集踢例如,试试这个:

<!html>
<html>
<head>
<title>Where goes memory?</title>
</head>
<body>

Greetings!

<script>
function init()
{
    var ref = window.setInterval(function() { draw(); }, 50);
}

function draw()
{
    var ar = new Array();
    for (var i = 0; i < 1e6; ++i) {
        ar.push(Math.rand());
    }
    return true
}

init();
</script>

</body>
</html>

当我运行它,我得到一个锯齿内存使用模式,(用今天的标准再次,非常小)围绕13.5MB波纹管峰值。

PS:我的浏览器的具体细节:

Google Chrome   23.0.1271.101 (Official Build 172594)
OS  Mac OS X
WebKit  537.11 (@136278)
JavaScript  V8 3.13.7.5
Flash   11.5.31.5
User Agent  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.101 Safari/537.11


Answer 6:

尝试没有匿名函数做这个。 例如:

function draw()
{
    return true;
}

function init()
{
    var ref = window.setInterval(draw, 50);
}

它是否仍然行为相同的方式?



Answer 7:

似乎没有成为一个内存泄漏。 只要内存使用GC后再次下降,整体内存使用率不向上的平均趋势,没有泄漏。

“真实”的问题,我在这里看到的是, setInterval确实使用的内存进行操作,并且它看起来并不像它应该分配什么。 在现实中,它需要分配的几件事情:

  1. 这将需要分配一些堆栈空间同时执行匿名函数和平局()例程。
  2. 我不知道是否需要分配任何临时数据进行调用自己(也许不是)
  3. 它需要分配存储的少量认为, true返回值从draw()
  4. 在内部,可以setInterval的分配更多的内存来重新安排重现的事件(我不知道它是如何工作的内部,它可能会重新使用现有的记录)。
  5. 在JIT可以尝试跟踪该方法中,这将分配额外的存储用于跟踪和一些度量。 虚拟机可以决定这个方法是太小,无法跟踪它,我不知道到底是什么所有的阈值是用于打开跟踪或关闭。 如果你运行这段代码足够长的虚拟机将其标识为“热”,它可以分配更多的内存来存放JIT编译的机器代码(在此之后,我预计平均内存使用率降低,因为生成的机器代码应该分配在大多数情况下较少的内存)

每次你的匿名函数执行有时间将是一些内存分配。 当这些拨款加起来某个阈值,GC将踢和清理,使您回落到基本水平。 该循环将持续下去,直到你把它关掉。 这是预期行为。



Answer 8:

我也有同样的问题。 客户告了我,它的计算机的内存被越来越多的增加每次。 起初我还以为是真的很奇怪,一个Web应用程序可以使,即使它是由一个简单的浏览器访问。 我注意到,这仅发生在Chrome中。

但是,我开始与合作伙伴进行调查,并通过Chrome浏览器的开发者工具和管理任务,我们可以看到内存增加客户报告了我。

然后,我们看到一个jquery函数(请求动画帧)中的溶液反复增加系统存储器加载。 在那之后,我们看到由于这个帖子,一个jQuery倒计时就是这么做的,因为它有一个“的setInterval”,每次被更新我的应用程序布局的日期内。

就像我跟ASP.NET MVC的工作,我只是退出从BundleConfig这个jQuery脚本倒计时,并从我的布局,用下面的代码替换我的时间倒计时:

@(DateTime.Now.ToString("dd/MM/yyyy HH:mm"))


文章来源: Does JavaScript setInterval() method cause memory leak?