So I'm working on this fairly complex real-time app. I have an audio engine that triggers heavy audio treatment functions every 100ms, to fill a buffer that is played right away. If these audio functions run a little too late, even once, you can hear the sound stuttering.
Everything works fine most of the time. However, when the garbage collector is triggered by V8, it takes around 150ms to complete a round, which triggers the aforementioned cracks.
So here is the question: how can I get shorter garbage collections?
Several remarks:
We could try to allocate fewer variables so that the heap grows more slowly, but I guess this would not solve the problem: even if the GC is triggered half as often, I don't see any reason why the collection should run faster. I'd rather have it run twice as often and last half as long.
I've read a few things about V8's GC. I know that I have no direct control over the GC. I understand that it has a short-term process and a long-term one (mark & sweep). I guess it's the latter one that causes the issue. However I don't know the exact cause of it taking so long: is it the amount of data that is deleted or the structure of the data that is browsed? Knowing this might help a little.
I've tried using the Chrome Dev Tools extensively, and according to this article I need to use the "Record Heap Allocations" module. But when I run one and look at the timeline in parallel, I can see that it triggers garbage collection all the time, pretty much like a heap snapshot, so I can't really track down what exactly is making my memory grow.
We don't have any memory leaks, this has been tested. There are no regular DOM accesses, no event listeners created. I guess this is a common issue, given the number of apps that have high FPS... Please help!
EDIT
One alternative solution we have thought of would be to store our data in a memory leak on purpose so that the GC does not run until we explicitly decide to release the memory. It sounds like an evil hack, has anyone ever done that? What do you think?
You could try to implement the "object pooling" design pattern. The basic principle is to reuse variables instead of creating an removing them. It's best to have one pool by object type.
Here is a very basic implementation.
var pool = [];
var poolRelease = function(obj) {
pool.push(obj);
};
var poolGet = function() {
// You could add parameters to the function to directly set the values of the object.
if (pool.length) {
return pool.pop();
}
// We don't have preexisting object, create it (can be anything).
return {x: 0, y: 0};
}
// Then in the code:
var myObj = poolGet();
myObj.x = 20;
// When finished with the object move it back to the pool to avoid garbage collection.
poolRelease(myObj);
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