我试图建立一个流畅的60fps动画浏览器JavaScript循环。我注意到垃圾收集器会启动并向动画帧添加非零时间变量。我首先跟踪代码中的分配,然后隔离循环本身。我在使用requestAnimationFrame
时发现,在一个所谓的“空”循环中,它仍然会导致每次迭代的分配,并触发垃圾收集器。令人沮丧的是,这似乎也发生在其他循环机制setInterval
和setTimeout
中。
下面我将一些jsfiddle和截图放在一起,演示示例“空循环”。所有样本均来自约5秒时间线。
此时,我正在寻找最小化垃圾收集的最佳解决方案。从下面的示例来看,requestAnimationFrame似乎是这方面最差的选择。
requestAnimationFrame
https://jsfiddle.net/kevzettler/e8stfjx9/
var frame = function(){
window.requestAnimationFrame(frame);
};
window.requestAnimationFrame(frame);
setInterval
https://jsfiddle.net/kevzettler/p5LbL1am/
var frame = function(){
//literally nothing
};
window.setInterval(frame, 0);
setTimeout
https://jsfiddle.net/kevzettler/9gcs6gqp/
var frame = function(){
window.setTimeout(frame, 0);
}
window.setTimeout(frame, 0);
2条答案
按热度按时间ql3eal8s1#
实际上我并不确定,但我似乎记得web worker有自己的垃圾收集器,因此GC命中不会影响主线程中的FPS(尽管它仍然会影响更新发送到主线程的能力)
62lalag42#
我不是Maven,但据我所阅读。我也遇到了你在评论中提到的相同的bug报告:
正如建议的那样,在每次调用时分配Number对象将与正在收集的垃圾相吻合。
https://bugs.chromium.org/p/chromium/issues/detail?id=120186#c20
它还建议,简单地打开调试器记录堆栈跟踪可能会导致问题。我想知道在进行远程调试时是否也是这种情况?
此答案建议在动画帧之间翻转,以减少垃圾收集:https://stackoverflow.com/a/23129638/141022
从你问的问题的深度来判断,我相信我要说的对你来说是显而易见的,但是你可能会有兴趣重新关注你的目标(尽管可能对你对Chrome的有趣观察没有帮助)。
我们需要记住的一件事是,我们的目标不是完全避免垃圾收集,因为它是JS的基础。相反,我们希望尽可能地减少它,以适应16 ms的渲染帧(以获得60 fps)。
VelocityJs的方法之一是使用一个全局“tick”来处理所有动画。
使用setInterval()、setTimeout()和requestAnimationFrame()时,将创建计时器。计时器创建有两个性能问题:1)同时触发的计时器太多会降低帧速率,因为浏览器维护它们的开销很大; 2)不正确地标记动画开始的时间会导致帧丢失。
Velocity对第一个问题的解决方案是维护一个全局滴答循环,该循环同时循环所有活动Velocity动画。不会为每个“速度”动画创建单独的计时器。简而言之,Velocity将调度优先于中断。
http://www.sitepoint.com/incredibly-fast-ui-animation-using-velocity-js/
这与减少垃圾收集的一般实践(例如创建回收缓存以重用对象,甚至重写方法(例如数组切片)以避免垃圾)一起使用。
https://web.archive.org/web/20120318031940/http://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript