three.js memory leak / browser crash - javascript

I am building an app with a spherical panorama photo with a jpg size about 4Mb. During development I am constantly refreshing the page I am working on to see changes, as well as opening multiple other three.js example pages to get tips. When Firefox first starts up, it uses around 250MB (I have a few tabs that open by default since it keeps crashing). When I refresh the page it jumps to around 420MB, then tapers slowly back down, but usually not to where it started from. If I refresh 2 times in 15 seconds or so, it will climb to nearly 500MB.
Usually I am not paying attention to my memory usage singe I have 32GB, but when firefox gets up around 2.5GB of memory used, along with around 15% processor usage, it starts bogging down and will crash. I would understand if my scene were particularly complicated, but it isn't, for the most part it is a single spherical pano, and one additional element I am working on at the time.
Is there something I should be adding to my code to clear out buffers between refreshes?

Seems this is a problem with TextGeometry without a solution.
https://github.com/mrdoob/three.js/issues/8478

Related

Workaround for bug on Chromium WebView about high load causing partial rendering on canvas

I found [1] this issue on newest WebView of Chrome. To produce it you need three things:
You have updated WebView to version from 15.11.2021
HardwareAcceleration in AndroidManifest is set true
When you heavily give stuff to render for webview, you start to see missing content on screen.
My technology stack uses a Openlayers map inside app that is actually a small website packed with Cordova, which is the component that uses the WebView.
By heavy use I mean the map has to have many layers of map data on for rendering and I have to make some maneavours moving the map and making it to start loading next rendered content before the previous one is completed.
At first time I open the map after installation, the map works ok with heavy load. Issue starts when I kill the app and login again and make the heavy movements when the app tries to render the first time. After that the map does not render content as a whole, some tiles and map menus come only partially.
The behavior continues even when I click off some map layers, and when there is very little of them anymore, the issue settles down. Interesting point is also, there is no single point where the load is enough to trigger the issue or settle down, at least the point is different going to each direction.
There is a warning message, that comes to logcat only when the issue is present:
chromium: [ERROR:tile_manager.cc(821)] WARNING: tile memory limits exceeded, some content may not draw
and I can observe it to be coming along same lines the 15.11.2021 version update has removed some "useless data" [2]. I am not 100% sure if these two things are related, but I have realized the versions before that particular update work just fine with my app (I removed updates from WebView in Google Play).
There are currently three workarounds:
Do not use map layers that are too heavy and trigger the issue (makes bad for our app user experience by hiding crucial information from user)
Remove webview updates from PlayStore (I personally tested that, overnight the system updated the broken version back, hard to communicate to end users).
Not to use hardware acceleration. (Other parts of the app get useless, too slow behavior makes end users angry).
As you observe none of these is working well. I believe that this same behavior is seen by other WebView heavy users too, so I opened this question with only one main question:
Meanwhile my bug report is advancing in Google, what could I do meanwhile to overcome the issue?
Sources:
[1] https://bugs.chromium.org/p/chromium/issues/detail?id=1274482
[2] https://chromium.googlesource.com/chromium/src/+/a0994781ea666d9b630b6478206ac429ed8444d1%5E%21/#F0

How to clear browser memory with Javascript

I have a simple single-page app that scrolls a bunch of photos indefinitely, meant to be run on displays for days at a time.
Because of the large number of scrolling pics, the memory use in Chrome keeps growing. I want a way to programmatically reduce the memory consumption at intervals (every few hours).
When I stop the animation programmatically, the memory footprint still doesn't go down. Even when I reload the page with location.reload();, it doesn't go down.
Is there a way to do this? How I can I "clear everything" programmatically that has the same effect as closing the tab?
FYI, in Firefox there isn't a memory issue. Just in Chrome. The code is super simple, uses requestAnimationFrame to animate two divs across the screen constantly. I'm not accumulating references anywhere. My question isn't about my code specifically, but rather about general ways to reset the tab's memory if it can be done.
Please find out with chrome or Firefox memory debugger, where the memory leak is.Then, when you find, just think, how you can clean this objects.
Reasonable causes of memory leaks are:
You are loading big images, you need to resize it on server, or simply
draw it to smaller canvases
You have too many dom elements (for
an example more than 10000)
You have some js objects, that are
growing up, and you don't clean it.
When in task manager you will see, that usage of memory is very high. You can see what going on with memory at firefox if you put to address bar about memory , and then press button "Measure".
You can use location.reload(true), with this it clears all the memory(caches etc). Moreover if you want to further clear the storage of a browser then you can use localStorage.clear() in your javascript code. This clears the items stored in the localStorage if you have saved something like this. localStorage.myItem = "something".

How to restart a canvas without reload

I simply want to restart a canvas on a website. I do not want to reload the page, so the restart has to be done dynamically. Problem : after few restarts the animation gets slower and the memory increases.
Actually i face a more complex situation : I use a javascript framework to load pages, MeteorJS with IronRouter. Changing the URL only changes the DOM (nodes are removed) but the 'page' is not really left. So i thought that coming back to my page with the canvas can be assimilated to a dynamic canvas restart, since i have no solution to that too.
That is why i describe the problem from two points of view (same problem on both) :
first a simple page where a button asks the canvas to restart
then applying the solution to MeteorJS with IronRouter and try to come back on my page many times.
Note : my canvas content is some WebGL (in ThreeJS).
1. Deleting and re-initializing canvas dynamically.
In this case i face those two problems :
animation not smooth
memory increase. To be precise :
(source: hostingpics.net)
Page loaded, playing with canvas / 2. Restarting the canvas 30-40 times (i did not stop restarting so i do not know why the increase is not linear) / 3. Playing with canvas / 4. Closing the tab (reloading the page only frees a small fraction of what has been mobilized)
Few questions on SO yet asked how to solve those issues, but reproducing the solutions partially solves the problem. I have read two main things :
the canvas-related parts (nodes, variables, functions) have to be cleaned from the DOM (That is something Meteor does. But in this first part it stays relevant and yet is not enough to prevent the issues).
if requestAnimationFrame has been called, it needs to get assigned to a variable so cancelAnimationFrame(variable) can stop it. That indeed seems to prevent the frame rate drop. While memory stays high, at least the movements remain fluent.
With those solutions the memory problem still happens.
Here is an example : http://codepen.io/Astrak/pen/RNYLxd
2. In MeteorJS : i cannot get this partial solution work.
Here is what i wrote in my app, with id=requestAnimationFrame(myAnimation) declared globaly :
Template.myTemplate.destroyed=function(){
cancelAnimationFrame(id);
document.body.removeChild(document.querySelector('canvas'));//useful in Meteor ?
};
Despite those instructions, my animation gets slow much quicker with MeteorJS than in the previous example : it becomes unusable after 3-4 page change. And same memory problem.
Thanks for every comment and answer and happy coding :)
Related questions :
Performance drop on multiple restart of scene from threejs
Performance drops when trying to reset whole scene with Three.js
How do we handle webgl context lost event in Three.js
Pause, resume and restart Canvas animations with JS
How can I restart my function?
JavaScript restart canvas script
For memory problem, I'd recommend Chrome's javascript profiler - you can compare snapshots before and after canvas is reloaded and see which objects are not freed from memory.
Problem usually comes from event handlers and/or using closures. If you, for example, hook a function to window object's onresize event, all objects referenced in this function will stay in memory as long as window exists. So always remove event handlers from global objects if you want to refresh page content without actually refreshing the browser session.
Similar with closures. They will create link between function's scope and referenced variables preventing GC to collect them. Check this https://www.meteor.com/blog/2013/08/13/an-interesting-kind-of-javascript-memory-leak

SDL2 - RenderPresent taking 20-30+ms randomly (in a Node.JS FFI call)

I've been updating a Node.JS FFI to SDL to use SDL2. (https://github.com/Freezerburn/node-sdl/tree/sdl2) And so far, it's been going well and I can successfully render 1600+ colored textures without too much issue. However, I just started running into an issue that I cannot seem to figure out, and does not seem to have anything to do with the FFI, GC, speed of Javascript, etc.
The problem is that when I call SDL_RenderPresent with VSYNC enabled, occasionally, every few seconds, this call will take 20-30 or more milliseconds to complete. And it looks like this is happening 2-3 times in a row. This causes a very brief, but noticeable, visual hitch in whatever is moving on the screen. The rest of the time, this call will take the normal amount of time to display whatever was drawn to the screen at the correct time to be synced up with the screen, and everything looks very smooth.
You can see this in action if you clone the repository mentioned above. Build it with node-gyp, then just run test.js. (I can embed the test code into StackOverflow, but I figured it would be easier to just have the full example on GitHub) Requires SDL2, SDL2_ttf, SDL2_image to be in /Library/Frameworks. (this is still in development, so there's nothing fancy put together for finding SDL2 automatically, or having the required code in the repository, or pulled from somewhere, etc.)
EDIT: This should likely go under the gamedev StackExchange site. Don't know if it can be moved/link or not.
Doing some more research online, I've discovered what the "problem" was. This was something I'd never really encountered before, (somehow) so I thought it was some obvious problem where I was not using SDL correctly.
Turns out, graphics being "jittery" is a problem every game can/does face, and there are common ways to get around it. Basically, the problem is that a CPU cannot run every process/thread in the OS completely in parallel. Sometimes a process has to be paused in order to run something else. When this happens during a frame update, it can cause that frame to take up to twice as long as normal to actually be pushed to the screen. This is where the jitter comes from. It became most obvious that this was the problem after reading a Unity question about a similar jitter, where a commenter pointed out that running something such as the Activity Monitor on OS X will cause the jitter to happen regularly, every couple seconds. About the same amount of time between when the Activity Monitor polls all the running processes for information. Killing the Activity Monitor caused the jitter to be much less regular.
So there is no real way to guarantee that your code will be run every 16 milliseconds on the dot, and that it will always ever be another 16 milliseconds before your code gets run again. You have to separate the timing for code that handles events, movement, AI, etc. from the timing for when a new frame will be rendered in order to get a perfectly smooth experience. This generally means that you will run all your logic fewer times per second than you will be drawing frames, and then predicting where every object will be in between actual updates, and draw the object in that spot. See deWiTTERS game loop article for some more concrete details on this, on top of a fantastic overview of game loops in general.
Note that this prediction method of delivering a smooth game experience does not come without problems. The main one being that if you are displaying an object in a predicted location without actually doing the full collision detection on it, that object could very easily clip into other objects for a few frames. In the pong clone I am writing to test the SDL bindings, with the predicted object drawing, if I hold right while up against a wall the paddle will repeatedly clip into the wall before popping back out as location is predicted to be further than it is allowed. This is a separate problem that has to be dealt with in a different way. I am just letting the reader know of this problem.

Clearing out (or preventing) JavaScriptCore objects building up in UIWebView?

I've got a UIWebView that is using way too much memory. The behavior in question involves an HTML/JavaScript page where you can re-color certain areas by tapping them, creating new color layers based on PNG files, but every time the user touches the field (whether they're adding color or re-coloring an existing area) the memory ticks up. Every touch eats up memory, and it never drops. This process continues ad infinitum, resulting in memory loss at best and an app crash at worst.
After playing around with Activity Monitor and Instruments, I've narrowed it down to a buildup of objects in the VM: Webkit malloc category, from the JavaScriptCore library. That makes sense: the page's functionality is JavaScript, and each PNG layer is about 4KB, the same size as the objects that are building up.
So now my question becomes, what do I do about it?
I'm kind of new to UIWebView, so I was wondering if someone could shed some light on my options? Is there a way to manually clear out all those unnecessary objects (I tried [[NSURLCache sharedURLcache] removeAllCachedResponses] but it did nothing), or prevent them from forming in the first place?
And perhaps most importantly: is this likely to end up being an iOS solution in the app, or a JavaScript solution on the page?

Categories