目前正在开发一个基于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:为了找出问题,我让上面的代码简单和愚蠢。
编辑: 尤里的回答是好。
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/
显然setTimeout
和setInterval
是没有正式的Javascript部分(因此他们不是V8的一部分)。 实施是留给实施者。 我建议你看一看的setInterval的执行情况,这样在node.js中
这里的问题是不是在代码本身,它不泄漏。 正是由于时间轴面板的实现方式。 当时间轴记录事件,我们收集的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
每次进行函数调用时,它会创建一个堆栈帧 。 不像许多其他语言,Java脚本存储在堆栈帧,就像一切。 这意味着每次调用的函数,你正在做的每50ms的时间,一个新的堆栈帧被添加到堆。 这就增加了,并最终垃圾收集。
它有点不可避免的,给了JavaScript是如何工作的。 能够真正做到以减轻它的唯一的事情就是让尽可能小的堆栈帧,这一点我敢肯定,所有的实现做。
我想你大概的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);
}
这将有助于一点,因为:
- 画()将不会直到它完成再由你的渲染循环中调用
- 因为许多上述答案指出,所有的不间断的功能从setInterval的调用都放在头顶上的浏览器。
- 调试有点容易,因为你不通过的setInterval不断发射中断
希望这可以帮助!
铬是很难从看到你的程序的任何内存压力(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
尝试没有匿名函数做这个。 例如:
function draw()
{
return true;
}
function init()
{
var ref = window.setInterval(draw, 50);
}
它是否仍然行为相同的方式?
似乎没有成为一个内存泄漏。 只要内存使用GC后再次下降,整体内存使用率不向上的平均趋势,没有泄漏。
“真实”的问题,我在这里看到的是, setInterval
确实使用的内存进行操作,并且它看起来并不像它应该分配什么。 在现实中,它需要分配的几件事情:
- 这将需要分配一些堆栈空间同时执行匿名函数和平局()例程。
- 我不知道是否需要分配任何临时数据进行调用自己(也许不是)
- 它需要分配存储的少量认为,
true
返回值从draw()
- 在内部,可以setInterval的分配更多的内存来重新安排重现的事件(我不知道它是如何工作的内部,它可能会重新使用现有的记录)。
- 在JIT可以尝试跟踪该方法中,这将分配额外的存储用于跟踪和一些度量。 虚拟机可以决定这个方法是太小,无法跟踪它,我不知道到底是什么所有的阈值是用于打开跟踪或关闭。 如果你运行这段代码足够长的虚拟机将其标识为“热”,它可以分配更多的内存来存放JIT编译的机器代码(在此之后,我预计平均内存使用率降低,因为生成的机器代码应该分配在大多数情况下较少的内存)
每次你的匿名函数执行有时间将是一些内存分配。 当这些拨款加起来某个阈值,GC将踢和清理,使您回落到基本水平。 该循环将持续下去,直到你把它关掉。 这是预期行为。
我也有同样的问题。 客户告了我,它的计算机的内存被越来越多的增加每次。 起初我还以为是真的很奇怪,一个Web应用程序可以使,即使它是由一个简单的浏览器访问。 我注意到,这仅发生在Chrome中。
但是,我开始与合作伙伴进行调查,并通过Chrome浏览器的开发者工具和管理任务,我们可以看到内存增加客户报告了我。
然后,我们看到一个jquery函数(请求动画帧)中的溶液反复增加系统存储器加载。 在那之后,我们看到由于这个帖子,一个jQuery倒计时就是这么做的,因为它有一个“的setInterval”,每次被更新我的应用程序布局的日期内。
就像我跟ASP.NET MVC的工作,我只是退出从BundleConfig这个jQuery脚本倒计时,并从我的布局,用下面的代码替换我的时间倒计时:
@(DateTime.Now.ToString("dd/MM/yyyy HH:mm"))