Javascript Objects and Memory Consumption
A while ago I read an article on different object creation patterns in Javascript. This article described how different patterns could consume more memory than others, and after reading it I wanted to build something to demonstrate what I had learned. I want to learn how to measure memory consumption in the browser, and I wanted to build something that would have a direct effect on my page’s memory consumption so that I could reliably demonstrate the negative effects of poorly written code.
Measuring Memory Usage in Chrome
In the Chrome dev tools, there is a Memory tab. Here you can take a snapshot of the JavaScript VM’s heap and see how much memory is being used by your page. There are more complex options for looking at memory consumption over time which is useful for identifying code that is using a lot of memory, but for this all we need is the Heap snapshot tool.
So, in Chrome, open up the dev tools now and click on the Memory tab:
Heap snapshot should already be selected, so click Take snapshot at the bottom. Medium’s post editor is using 11.5 MB of memory:
Spamming Objects to Consume Memory
To demonstrate some things we need a way to eat memory, so here is our contrived example!
Head on over to https://memorydots.netlify.app/ to see the above in action and to follow along with the examples.
The code spawns a 5x5 square at a random position within the bounds of the canvas, then each dot calls a function every frame that moves it in a random direction. The color of each dot is based on its position which has no relation to the topic at hand; it was just really boring when all the dots were one color.
So What Are We Testing?
We’re going to be creating many dots using different implementations to show how these implementations consume different amounts of memory. So, here is a very simple dot:
There’s not much going on here. The dot generates a starting position, and updatePosition
moves it slightly while constraining it to the bounds of the canvas. The code could be simplified, but we’re trying to be wasteful here so let’s put on our cowboy hats and see how much memory these bad boys take up.
Back over to https://memorydots.netlify.app/, if we type 100000
into the Number of Dots
text box and hit Create Simple
then if your computer is anything like my laptop things will start to get a bit sluggish.
If we take a snapshot of the heap now, we can see how much memory our 100,000 dots are taking up:
13.3 MB. An exorbitant amount of memory. Let’s see if we can leverage the power of inheritance to lower this to a more reasonable amount.
Javascript Prototypes and Inheritance
Behold, the same code in a slightly different shape:
Here we have a function, InheritDot, that when run creates a randomized starting position for itself. It then adds the updatePosition
function to its prototype instead of containing it itself. All instances of InheritDot will inherit from the same prototype object, meaning that updatePosition
will only exist once regardless of how many dots we make.
Let’s take a look at the memory consumption of 100,000 Inherit Dots. If you’re following along on https://memorydots.netlify.app/, set the count to 100000,
click Create Inherit
and you should see something like this:
By Grabthar’s Hammer, what a savings! At 7.7 MB we’re using 42% less memory to achieve exactly the same thing.
A side note: We could have improved the Simple Dot by converting updatePosition
into a separate module and importing it in like so:
import random from 'lodash.random';
import updatePosition from 'modules/updatePosition';
function SimpleDot(ctx) {
this.x = random(0, ctx.canvas.width);
this.y = random(0, ctx.canvas.height);
this.updatePosition = updatePosition;
}
export default SimpleDot;
It’s still storing a reference to updatePosition for each dot, but at 100,000 dots I was seeing it take memory consumption from the 7.7 MB of the Inherit Dot to 8.1 MB. Much better than 13.3 MB, but it’s worth remembering that even references can take up space.
JavaScript Classes
Controversial to some yet existing regardless, the class
keyword brings JavaScript kicking and screaming into the 1960s.
Here is one of our dots implemented in a more Object-Oriented fashion:
We have the class Dot
with an update function and a class ClassDot
which extends Dot
and is only responsible for its initial position. Essentially the same as Inherit Dot, but how does it stack up in memory consumption? On https://memorydots.netlify.app/ make sure the dot count is still 100000
and hit the Create Class
button:
Identical. When we have a peek under the hood, we can see why:
Both InheritDot
and ClassDot
have updatePosition in their prototype chain. Classes in JavaScript are a fancy wrapper around regular prototypical inheritance so fundamentally nothing has changed compared to the InheritDot.
Neato!
Yeah! Now you know how to look at memory usage in Chrome, how to write some deliberately wasteful code, and how important it can be to understand what JavaScript is doing under the hood.
The source for the MemoryDots example is available here: https://github.com/terrarum/memorydots