I'm building an app that contains a WebView that runs some JavaScript code. That JavaScript code is quite allocation heavy and can require a lot of memory.
Sometimes, the amount of required memory exceeds the amount JavaScript can require and crashes the Chromium process of the WebView which crashes my app.
I listen to onMemoryTrim in my application - but it is never called in this scenario on devices with more than 1GB of memory. (Not even with TRIM_MEMORY_RUNNING_LOW).
Is there any way I could detect my WebView is running low on memory and either kill it or let it know (so it can free memory)?
I've tried polling performance.memory but it did not work. The following script crashes the WebView if executed in it:
var a = [];
var kek = () => {
var b = [];
for(var i = 0; i < 1024 * 1024 * 2; i++) b.push(Math.random());
return b;
}
var ival = setInterval(() => {
let m = performance.memory;
if(m.jsHeapSizeLimit - m.usedJSHeapSize < 1e5) {
console.log("Memory limited")
} else {
a.push(kek());
}
});
Is there any way to detect memory is about to run out so I can handle it gracefully without the app crashing?
I have discussed this with the Chromium team and the Android team and at the moment (they think and I believe them) that this is impossible.
Sometimes, the amount of required memory exceeds the amount JavaScript can require and crashes the Chromium process of the WebView which crashes my app.
You can however catch out of memory crashes in Android 8.0+ using the new termination handle API. So this works around my problem by not having to check the available memory required in the first place.
By overriding onRenderProcessGone - we get to catch the bug and recreate the WebView.
Related
I am trying to allocate memory in JavaScript to study memory leak/consumption using the code snippet below. However
performance.memory.usedJSHeapSize
always shows the same number, 10000000 in my case. How come that number never changes despite dynamically creation of elements and attaching to DOM ?
I need a JavaScript snippet to create memory leak and monitor the usage using performance.memory.usedJSHeapSize dynamically( or any other functions if exists).
I tried this code but performance.memory.usedJSHeapSize remains at 10000000:
<body>
<p id="memory" style="position: fixed; top:10px; left:10px"></p>
<script>
setInterval(() => {
document.getElementById("memory").innerHTML = performance.memory.usedJSHeapSize
}, 300);
btn = [];
let i = 0;
setInterval(() => {
for (let j = 0; j < 1000; j++) {
++i;
let k=i;
btn[k] = document.createElement("BUTTON");
document.body.appendChild(btn[k]);
btn[k].innerHTML = k;
btn[k].addEventListener("click", function () {
alert(k);
});
}
}, 5000);
</script>
</body>
I already tired the example given in 2013 in this post, but this one no longer create memory leak either.
How do I create a memory leak in JavaScript?
performance.memory.usedJSHeapSize does not update when the page is opened directly from the local file system.
The image below shows that the exact same code copy-pasted from the question shows increasing memory usage when accessed at localhost:
Or, you can check for yourself: https://memory-leak.surge.sh/simple/ (You can also check the original code: https://memory-leak.surge.sh/ but your browser might freeze up if left open for more than a few seconds.)
How to host the HTML like I did above:
The simplest option is to use dev tools like Browsersync or Parcel. These tools will let you open files from your local file system as if they were hosted from a server with a URL like http://localhost:1234/ . (Because a temporary web server is started on your computer.)
Another option is to actually host the files on a server. There are many options to do this:
surge The tool I used for the examples above
Glitch (This one is cool because you can edit the files online and see changes right away)
Github pages
Note: results may vary based on browser/hardware. My environment:
Chrome Version 74.0.3729.131 (Official Build) (64-bit)
Windows 10
Nodejs has a built in OS module that we can use by having this line in our code
var os = require('os');
There are a number of functions to use such as getting free memory, total memory, cpu usage, load average, etc.
My question here is HOW does nodejs calculate this information?
For example, the free/total RAM memory, how is that being done under curtains. Is it calling another process to read stats from the system? is it running a separate command like iostat or dstat? How is it actually retrieving that data for us?
The os.totalmem function is a native function from process.binding('os') called getTotalMem. Their implementations can be found in the Node source code:
The following code can be found in src/node_os.cc:
static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {
double amount = uv_get_total_memory();
if (amount < 0)
return;
args.GetReturnValue().Set(amount);
}
// ...
env->SetMethod(target, "getTotalMem", GetTotalMemory);
The uv_get_total_memory function has several implementations based on the host OS.
Here is the Linux implementation deps/uv/src/unix/linux-core.c:
uint64_t uv_get_total_memory(void) {
struct sysinfo info;
if (sysinfo(&info) == 0)
return (uint64_t) info.totalram * info.mem_unit;
return 0;
}
The Linux build uses sysinfo to get this information. It does not need to spawn another another process.
I'm in a project where I am building a simulator of a website. I am testing how feature toggling can provide some cons that can help a team release more often than they do now.
One thing I do like to simulate is how the Canary release is working. Lets say I just finished building a new feature and I need to have it tested in production. Canary release is just to push this feature out to a small number of users.
How do you simulate this with code? I'm building the applikation with angular2 anad with typescript. Have created configurationfiles for the features that I can use.
How do you, lets say pick only 5 percents of random people that visit the site to test the specific feature? Is it all done with server configuration (running another build at a different server).
If any could make a code example of how I could simulate this when the application starts, I've be happy.
Have made this code myself:
var switchKey: string = localStorage.getItem('featureSwitch');
if (this.featureSwitch != null) {
if (switchKey == "11") {
this.featureSwitch = 1;
localStorage.setItem('featureSwitch', this.featureSwitch.toString());
}
}
else {
if (switchKey != null) {
if (switchKey == "11") {
this.featureSwitch = 1;
localStorage.setItem('featureSwitch', this.featureSwitch.toString());
}
else {
this.featureSwitch = Number(switchKey) + 1;
localStorage.setItem('featureSwitch', this.featureSwitch.toString());
}
}
else {
this.featureSwitch = 1;
localStorage.setItem('featureSwitch', this.featureSwitch.toString());
}
}
This is maybe a bad example, cause I don't think it will work on a live site (on the internet), this is only tested on a localhost server. Basically I'm saving a number from 1-11 in localstorage, where I can show a feature based on one or more numbers.
Any have some ideas how I could do this easily?
Basically I'm saving a number from 1-11 in localstorage, where I can show a feature based on one or more numbers.
You should do canary releases based on users (not browser sessions). Otherwise the user will be surprised as they switch browsers / devices / locations. And you will not know which users are participating in a test (a user can AND can't be in the test if they use two devices).
This needs server side support, the switch belongs in the server.
I have started looking at tutorials for making TVML/TVJS based apps for the new Apple TV, and I have two problems that makes the development process very tedious and impractical.
First thing I am having trouble understanding is how I am supposed to debug code that happens on startup of the application. I have connected the Safari debugger, and I do manage to hit some breakpoints, but only for code that is triggered by some user input. On startup I am loading an xml document from a remote location, and I will use this to dynamically generate the tvml template, but I cannot get the debugger to stop anywhere in the code that is running before the template is done rendering.
The other anti-productive problem I have is that I cannot seem to reload the JavaScript files in any other way than completely shutting down the application in the simulator (double-click the home button, and swipe the app away). This also makes the debugger quit, so I have to restart that one as well. This surely cannot be the way you are supposed to do continuous development and testing?
You can make the debugger stop at the first line when you choose the Auto Pause and Auto Show options from the Safari menu "Develop/Simulator".
You are correct about the exit issue.
One thing you can also try is to run App.reload() from the Safari Debugger console.
This also restarts the app, maybe in the future they can make it work so the debugger will not be gone.
But at the moment this also does not solve the issue.
For manual debugger output (aka console.log()), you could redirect the logging to the Xcode debugger.
(somewhere on the web) I found a way to actually do that, in short it looks like...
AppDelegate.Swift
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
let jsInterface: cJsInterface = cJsInterface();
jsContext.setObject(jsInterface, forKeyedSubscript: "swiftInterface")
}
App.js
// re-route console.log() to XCode debug window
var console = {
log: function() {
var message = '';
for(var i = 0; i < arguments.length; i++) {
message += arguments[i] + ' '
};
swiftInterface.log(message)
}
};
JsInterface.Swift
#objc protocol jsInterfaceProtocol : JSExport {
func log(message: String) -> Void
}
...
class cJsInterface: NSObject, jsInterfaceProtocol {
func log(message: String) -> Void {
print("JS: \(message)")
}
}
Complete sources in github: https://github.com/iBaa/PlexConnectApp/tree/f512dfd9c1cb2fbfed2da43c4e3837435b0b22af
I don't have any solution for the dying debugger myself...
I want to do some stress test against my websocket server runnig this javascript example in my browser (chrome) below:
function create_ws() {
var ws=new WebSocket("ws://127.0.0.1/");
ws.onopen=function(evt){
var binary = new Uint8Array(2);
binary[0]=1;
binary[1]=2;
ws.send(binary.buffer);
};
ws.onclose=function(evt){};
ws.onmessage=function(evt){};
ws.onerror=function(evt){};
}
var connections = [];
var numberOfconnections=100;
for(var i = 0; i < numberOfconnections; i++) {
connections.push(create_ws());
}
The problem is the script above let me run only about 100 connections at the same time. If i increase numberOfconnections to 300 it throws following error:
WebSocket connection to 'ws://127.0.0.1/' failed: Error in connection establishment: net::ERR_INSUFFICIENT_RESOURCES
Is there a way to increase the number of websocket connections in browser?
Try to open new tabs with your stress test manually or with window.open in code.
Looks like the limit for chromium is set to 256 per the discussion below:
Chromium Discussion
It's possible other browsers have different limits. As I only use so many connections for stress testing, I plan to open a few windows to go outside the limit.