Empty requestAnimationFrame loop leaking memory? - javascript

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:
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>

I investigated this too. I came here because I noticed Chrome is tracking where calls come from.
And I noticed it was going all the way back to the 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?
¯\_(ツ)_/¯

Related

How to find out what's causing my Javascript to crash the browser tab?

I'm making a rather extensive game using Javascript. It is a kind of online game maker that allows players to upload media files and use them to create worlds. Unfortunately, it is rather prone to crashing the browser tab at unpredictable moments. So far, I have found no pattern to this - sometimes it happens within a few minutes, other times it can run for hours without a problem.
I have tried enabling logging in Chrome, but the crashes don't seem to generate an error report in the chrome_debug file.
I thought it might be that the program was using too much memory (given the game's open-ended nature, some worlds can involve downloading rather large data files - though this seems unrelated to when the crash actually happens - while large worlds do seem to be more crash-prone, they do not always crash when the world's data is loaded).
I tried using Electron to turn it into an executable app, but the app still crashes. That shouldn't happen if it's a memory issue, right?
Is there any way of finding out what is causing the code to crash?
Most unpredictable crashes in Javascript are caused by memory leaks - objects that are still stored in memory and not being picked up by the garbage collector. Every object in Javascript is stored in a variable somewhere within the global scope, or is associated with another object that is itself connected to the global scope. When a "branch" of the "tree" is removed and can no longer be accessed by the global scope, the garbage collector destroys it.
However, if an object is not being removed from the global scope when it should be, it remains in memory. This usually happens when objects are added to an array but are not removed from that array when they are no longer in use. Over time, these objects build up until the process crashes due to memory overload.
To find memory leaks in Chrome, press F12 and open the Performance tab. By recording the page over time, you can view the amount of memory being used. The green line (nodes) is the most important here - it refers to the number of objects in memory. If nodes are constantly increasing over time (there will always be increases and decreases, but if the overall level is constantly rising) this generally means there's a memory leak.
To find which specific objects are causing the problem, open the Memory tab to take snapshots or timeline profiles of the memory heap. This gives you a count of the specific objects that are in memory at any given time. If there are more of some kind of object than there should be, that's where the leak is.

Javascript - Preventing Chrome From Killing Page during long loop

Chrome keeps killing the page in the middle of my connect-four browser game when it is running properly. The game is a player vs computer setup and the game itself runs properly and never crashes. The page crashes when I set the number of iterations too high for training the computer opponent. The programs trains the ai using a qLearning algorithm where it plays itself and stores a value for each encountered state. If I set the number of iterations to about 125,000 or less, then everything works fine (except the opponent is not so great). I cannot tell if it is the running time of the loop (would take about 30 minutes to run) that kills the program or something else such as memory constraints for recording states and their corresponding q-values.
How can I get the program to run for more training iterations without chrome killing the page?
You've got a couple of options on how to handle your code.
Option 1: setInterval / setTimeout
As others have suggested, using setInterval or setTimeout can run your code in "chunks" and no one chunk will cause a timeout.
Option 2: setInterval + generators
With deeply nested code, using setTimeout is very difficult to properly re-enter the code.
Read up on generators -- that makes running code in chunks much nicer, but it may take some redesign.
Option 3: webworkers
Webworkers provide another way, depending on what you are calculating. They run in the background and don't have access to the DOM or anything else, but they are great at calcuation.
Option 4: nodejs
Your last option is to move away from the browser and run in another environment such as node.JS. If you are running under Windows, HTA files may be another option.

Javascript how to create an allocation free animation loop to avoid garbage collector?

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);
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

UIWebView Javascript garbage collection with THREE.js scenes

I'm developing a game with THREE.js that will run inside UIWebView inside an app on iOS8.
I've profiled this application in Chrome's developer tools and ensured that there are no memory leaks - that is - memory use gets up to a certain value and remains constant throughout.
Running this application in UIWebView, however, reveals that the memory use grows over time, as if no garbage collection takes place at all.
I've searched online, but can not determine conclusively whether iOS8 UIWebView has garbage collection or not. Some articles seem to suggest it does, and some that it does not. If it does - how can I trigger this?
The only solution I can imagine at this time, if there is no garbage collection is periodically killing / deallocating UIWebView, and recreating/restarting the app (a game at the menu screen) .
UPDATE:
After spending a few more days looking for leaks here's what I found:
Deallocating UIWebView doesn't work - system never deallocates everything (even with all the suggested hacks) and memory problems get compounded.
I still don't know if UIWebView has mark/sweep garbage collection - the Profile/Instruments panel seems to suggest it does, but the memory use rarely ever goes down. Common sense tells me that some garbage collection must take place, because all the temporary objects in code, and things going out of scope do get cleaned up.
My THREE.js objects do not seem to ever be collected - but this may be related to THREE.js own issues of having to manually dispose of resources (in order to free any GL-related handles etc.)
There are mysterious leaks related to .bind(this) - example setTimeout(object.func.bind(object),100) - will apparently never clean up the function after timeout dispatches - so I end up pre-binding and storing it as a var instead. Same goes for any even handlers passed to jQuery.
My 2 scene game (menu scene and game scene), I ended up rewriting in such a way that both scenes remain in memory (never get removed). Any objects and models I create in the game, instead of relying on GC to get collected, get recycled instead. When objects are removed from the scene, they are put in a pool of objects of the same type to be re-initialized and re-added when such object is required in the scene again. It seemed like an overkill at first, but the benefit is - no memory leaks (objects remain allocated), and faster creation/adding to scene.
The memory grows because some resources are cached in the memory due to HTTP cache strategy. So if the memory does not grow infinitely, don't worry about that.
What you are seeing is probably a bug in iOS8 where the garbage collector never frees up memory. I posted about it here: PhoneGap using way more memory in iOS8 than iOS7
If you can please file a bug with Apple so we can get this fixed in the next OS release.

Is there a way to control Chrome GC?

I am working with quite large volume of data.
Mechanism:
JavaScript is reading WebSQL database, then assembles data into Object that has tree structure.
Then applies to tree object knockout.js (makes elements observable) then data-binds
and then applies Jquery Mobile UI at the end.
Whole process takes unacceptable amount of time.
I have already optimized algorithm that makes tree object out of data,
also optimised conversion to observables mechanism by pushing items directly into ko.observable arrays and calling hasMutated only once.
I am applying knockout.js IF bindings to not process invisible tree nodes in UI until parent is opened.
Performance here is key.
After inspecting page load in timeline in Chrome developer tools I have noticed that Garbage Collector is doing cleans on every concurrent call when I am building tree object.
Question: Is there a way to temporarily disable Chrome GC and then enable it again after I am done with page processing?
P.S I know I could add reference to part that gets collected, basically introduce object that dominates and prevents GC collection, but this would require substantial changes through the code, and I am not sure I could keep it long enough, and it is likely to introduce memory leak. Surely there must be better way
No, there is no way to disable the garbage collector. There cannot be, because what is Chrome supposed to do when more memory is requested but none is available?
(Also, the garbage collector is very fine-grained and complicated; your screenshot is a bit too small to be readable, but in all likelihood what you're seeing are small steps of incremental work to keep up with allocations, and/or "minor GC" cycles that only operate on the relatively small area of the heap where new allocations happen.)
If you want to reduce time spent in GC, then the primary way how to achieve that is to allocate fewer and/or smaller objects. Yes, that can mean changing your application's design so that objects are reused instead of being short-lived, or similar changes in strategy.
If you allocate a lot, you will see a lot of GC activity, there is just no way around that. This is true even in languages/runtimes that are not considered "garbage collected", e.g. in C/C++ using new/delete a lot also has a performance cost.

Categories

Resources