I'm trying to build a smooth 60fps animation browser javascript loop. I've noticed that the garbage collector kicks in and adds variable non-zero time to animation frames. I started by tracking down allocations in my code and then isolated the loop its self. I was using requestAnimationFrame
and discovered that on a supposedly 'empty' loop It still causes allocations each iteration and triggers the garbage collector. Frustratingly this seems to happen in other looping mechanisms setInterval
and setTimeout
as well.
Below I've put together some jsfiddles and screenshots demonstrating the sample 'empty loops'. All the samples are from ~5 second timelines.
At this point, I'm looking for the best solution to minimize garbage collection. From the samples below it looks like requestAnimationFrame is the worst option in this regard.
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);
Garbage collection is performed automatically. We cannot force or prevent it. Objects are retained in memory while they are reachable. Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we've seen in the example above.
Some high-level languages, such as JavaScript, utilize a form of automatic memory management known as garbage collection (GC). The purpose of a garbage collector is to monitor memory allocation and determine when a block of allocated memory is no longer needed and reclaim it.
When it comes to programming, Garbage Collection means cleaning the memory spaces which don't contain useful data and then reallocating those cleared memory to some other data which is both active and useful. That is the basic process of Garbage Collection in pretty much all the programming languages in the world.
Heap: It is used to store objects and functions in JavaScript. The engine doesn't allocate a fixed amount of memory. Instead, it allocates more space as required.
I'm not actually certain, but I seem to remember that web workers have their own garbage collectors, and so the GC hit wouldn't affect FPS in the main thread (though it would still affect updates' ability to be sent to the main thread)
I'm no expert, but from what I've been reading. I too came across the same bug report that you mentioned in your comments:
As suggested, allocating the Number object on each call, would tally with garbage being collected.
https://bugs.chromium.org/p/chromium/issues/detail?id=120186#c20
It also suggested that simply having the debugger open recording the stack traces could cause problems. I wonder if this is the same case when doing remote debugging?
This answer suggests flip flopping between animation frames, to reduce the garbage collection: https://stackoverflow.com/a/23129638/141022
Judging by the depth of question you have asked, I'm sure what I'm about to say is obvious to you, but it might be interest to refocus towards your goal in general (albeit perhaps doesn't help with your interesting observation of Chrome).
One thing we need to remember is that we're not aiming to avoid garbage collection completely as it's so fundamental to JS. Instead we are looking to reduce it as much as possible to fit into rendering our frames with 16ms (to get 60fps).
One of VelocityJs's approaches is to have a single global "tick" that handles all animation...
Timers are created when setInterval(), setTimeout(), and requestAnimationFrame() are used. There are two performance issues with timer creation: 1) too many timers firing at once reduces frame rates due to the browser’s overhead of maintaining them, and 2) improperly marking the time at which your animation begins results in dropped frames.
Velocity’s solution to the first problem is maintaining a single global tick loop that cycles through all active Velocity animations at once. Individual timers are not created for each Velocity animation. In short, Velocity prioritizes scheduling over interruption.
http://www.sitepoint.com/incredibly-fast-ui-animation-using-velocity-js/
This along with general practices on reducing garbage collection such as creating a recycling cache to reuse objects or even rewriting methods such as array slice to avoid garbage.
https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With