Blog Home » Monitoring JavaScript Memory

Monitoring JavaScript Memory

By Todd H Gardner

I was chatting with Dominik Kundel from Twilio, who had built a cool application to manage the coffee lines at NDC Oslo. You could SMS your order in and they displayed tons of analytics about the coffee served. It was a really great demonstration of their technology, and it pushed the boundaries of what you can do with a webapp.

But it had some memory problems.

We put TrackJS on it, but memory leaks are tough to monitor. Ultimately, they can crash the browser, and our tracking code along with it. But this is an important problem than many projects run into, especially as our webapps become more ambitious.

window.performance.memory is a propriety extension in Google Chrome that offers a glimpse into how our JavaScript applications are using browser memory. It exposes the following metrics:


{
  totalJSHeapSize: 29400000,
  usedJSHeapSize: 15200000,
  jsHeapSizeLimit: 1530000000
}

With these, we can extrapolate some high-level idea of how our application is using memory. But if you give it a try in your browser console, you may notice that the numbers don’t change. This is a security protection–Chrome doesn’t want to expose its internals to just anyone who might be listening. To enable more accurate memory monitoring, start Chrome with the --enable-precise-memory-info flag.

Starting with custom flags? That’s a tough one. It’s real unlikely that visitors to our webapps have this turned on. But there are a certain class of apps where you might have control of it: intranets, kiosks, or debugging with known customers.

Assuming we can gather this information, we can ask some interesting questions as part of our monitoring. How much memory are we using currently? How close is it to the limit? These can help us understand how our applications perform in the real world, with real users, and with real use-cases (as long as they’re using Chrome anyway).

Here’s one way we could sample memory usage and feed it into a monitoring tool (like TrackJS). This example uses requestAnimationFrame to periodically check the memory usage and record an error if it has crossed either an absolute or relative threshold.


(function() {
  // Tune these for your application.
  var MAX_MEMORY_LIMIT = 20 * 1048576; // 20MB
  var MAX_PERCENT_THRESHOLD = 90;

  if (!window.performance || !window.performance.memory || !window.requestAnimationFrame || !window.trackJs) { return; }
  var hasAlarmed = false;

  requestAnimationFrame(function onFrame(){

    // Check if we have exceeded absolute memory limit
    if (performance.memory.usedJSHeapSize > MAX_MEMORY_LIMIT) {
      hasAlarmed = true;
      var overage = performance.memory.usedJSHeapSize - MAX_MEMORY_LIMIT;
      trackJs.track(new Error("Exceeded memory maximum limit by " + overage + " bytes"))
    }

    // Check if we have exceeded relative memory limit for client
    if (performance.memory.usedJSHeapSize > (MAX_PERCENT_THRESHOLD/100) * performance.memory.jsHeapSizeLimit) {
      hasAlarmed = true;
      trackJs.track(new Error("Memory usage exceeded " + MAX_PERCENT_THRESHOLD + "% of maximum: " + performance.memory.jsHeapSizeLimit))
    }

    // Only alert once
    if (!hasAlarmed) {
      requestAnimationFrame(onFrame);
    }
  });
})();
Example memory monitoring extension for TrackJS

I’ve been playing with this on some of my apps over the last few hours. It works pretty well, I was able to trigger errors when I did silly things to explode the browser memory. I like that it is adjustable: I could set a known maximum based on my development-time testing, or I could simply check to make sure I don’t exceed the high level of memory allocated to the Chrome process.

Memory usage could also be a useful piece of metadata about other JavaScript errors that occur. Perhaps your application exhibits a different set of timings under heavy memory pressure, or fails in unique ways. I put together another example on how we could feed in memory information to other errors that we capture, using the TrackJS onError callback.


TrackJS.install({
  token: "YOUR_TOKEN",
  onError: function(payload) {
    if (window.performance && window.performance.memory) {
      payload.console.push({
        // constructing JSON because performance.memory does not stringify
        message: "{" +
          "totalJSHeapSize:" + performance.memory.totalJSHeapSize + "," +
          "usedJSHeapSize:"  + performance.memory.usedJSHeapSize  + "," +
          "jsHeapSizeLimit:" + performance.memory.jsHeapSizeLimit +
        "}",
        severity: "info",
        timestamp: new Date().toISOString()
      });
    }

    return true;
  }
});
Example memory information extension for TrackJS

Both of these examples seem pretty useful to debug modern interactive web applications, but it’s limited to Chrome with custom flags today. If you’re building something like Twilio’s demo, maybe you can make use of it. Hopefully the utility of this spreads.

If you need a way to record your memory usage errors, and JavaScript errors of all kinds, grab a free 14-day trial of TrackJS! You’ll be amazed by what you can fix.

Todd H Gardner
Todd H Gardner
CEO and Cofounder