12

I have a clean HTML file with requestAnimationFrame loop that does absolutely no processing. However, if I look at memory consumption on Chrome DevTools I see that used memory constantly increases and garbage collector runs every few seconds to collect around 1 megabyte of garbage data.

So where does this memory leak comes from?

That's how my memory usage looks like:

Chrome DevTools memory usage data

And here's my code:

<!DOCTYPE html>
<html>
<head lang="en">
    <title></title>
    <script>

        function update() {

            window.requestAnimationFrame(update);

        }

        update();

    </script>
</head>
<body>

</body>
</html>
GiedriusT
  • 400
  • 3
  • 13
  • does the memory climb stop when you comment out the do-nothing loop? – dandavis Oct 15 '15 at 09:12
  • yes, if I comment out the initial launch of update() function the climb doesn't happen – GiedriusT Oct 15 '15 at 09:16
  • interesting, but i would offer that it's not a leak if the memory is returned when GC is done. there is some work to be done every time a function executes, and that probably leaves invisible crumbs internally, be it from optimization attempts, a closure artifact, or some kinda counter for rAF. it doesn't seem like a deal-breaking amount, so perhaps you've found the minimum investment required to use rAF – dandavis Oct 15 '15 at 09:26
  • but don't you think that a megabyte of garbage every few seconds is quite a big deal. I came to investigating this because I'm making a project where I need things to run at 60fps. And things were jittery on mobile Chrome. Then I noticed that slow frames appear at the same time the GC runs. And then I started investigating why it runs so often. My code had issues so I fixed them and managed not to create any objects or variables in the main loop, but still got the jitters. So then i stripped down everything and found out that somehow requestAnimationFrame is generating all this garbage. – GiedriusT Oct 15 '15 at 09:35
  • personally, i don't see a 1mb fluctuation itself as a big deal in the programming scheme of things, though it may stall animations as you've observed. i would offer that movies have used 24FPS for a hundred years, to good effect. i would also imagine that faster devices will be less jittery, so your code might just be ahead of its time. sounds like you're on the right track reducing object creation and using profilers. can i ask you an aside: did reducing the object creation help at all, or was it about that same after? – dandavis Oct 15 '15 at 22:00
  • I didn't notice a major change from reducing object creation. But then again my initial code wasn't that bad. I only had to change a few places where I was creating new object every tick to return values from the function (like return {...}). So that's negligible compared to what requestAnimationFrame itself creates. – GiedriusT Oct 16 '15 at 09:24
  • you are right about movies running at 24FPS, but when it comes to computer screens and sub-pixel accuracy animation that you are looking at from a small distance, 60FPS really adds a perceived smoothness compared to 30FPS. It's a standard in gaming for a long time and for good reasons. However I will try to deliberately lower the rate at which requestAnimationFrame is invoked and see how it affects things. – GiedriusT Oct 16 '15 at 09:37
  • interestingly if you add `cancelAnimationFrame(id) providing the id returned by raF it will increase heap size in profiler... and GC will be called quicker... – mtx Mar 25 '23 at 23:08

1 Answers1

3

I investigated this too. I came here because I noticed Chrome is tracking where calls come from.

show devtools showing call chain

And I noticed it was going all the way back to the original call

devtools traced back to original call

So, I left it running for an hour checking on it every few minutes. It did allocate memory for a while but eventually seemed to release some.

I'm pretty sure it does this to make debugging other async code easier since it's helpful to know where some async request started. Without that path all you get is that your event/callback got called, not where it was created.

That said I understand your pain. It's well known that 24fps has problems and movie directors tend to avoid the types of scenes that show those issues. When "The Hobbit" came out and was shot at 48fps the director tried adding some of the types of scenes back in that fail at 24fps.

It's also well known in video games that 30fps is not enough for any 2d scrolling game. For into the screen games it's passable (whole generations of games shipped at 30fps on PS1, PS2, N64) but for 2d scrolling games the entire screen appears to shutter at 30fps where as it appears smooth at 60fps

In any case I don't have a solution to suggest. It's just the way the browser works. Every time you create an event, which is what requestAnimationFrame does, some small object has to be allocated and put on the list of events to be executed at some point in the future and that in itself takes memory. For requestAnimationFrame in particular maybe the browser could special case having only a pre-allocated queue of a few events and trying to recycle them rather then use the generic system they use for all other events (mouse, keyboard, images loading, XHR requests finishing etc) but that would likely be hard to implement given that when the events actually execute they probably need to be in the same queue.

And even then, JavaScript itself is all about allocating memory. It's nearly impossible not to. Calling a function allocates the arguments since they end up being part of that function's closure context (JS engines might optimize that but from language perspective they allocate).

Now that browsers are starting to target WebVR which requires 90fps maybe they'll come up with a new way. Or maybe we'll all switch to WebAssembly?

¯\_(ツ)_/¯

gman
  • 100,619
  • 31
  • 269
  • 393
  • although now that I think about it WebAssembly won't solve this either. By design a new object would have to allocated even for WebAssembly to use `requestAnimationFrame`. – gman May 31 '18 at 04:07
  • interesting thoughts, it seems things haven't changed in two and a half years since I started this thread. And I do agree, with things like WebVR, they will have to optimise these little things, because it's super important. – GiedriusT Jun 01 '18 at 08:58