Objects, Prototypes and Memory Consumption

I recently read an article called Understand JavaScript Object Creation Patterns. The author talks about object prototypes and how declaring a function on an object will increase memory consumption versus putting that function on the object’s prototype, assuming you have more than one instance of the object.

This sounds quite obvious, but I became interested in learning how to prove this using Chrome’s developer tools, so I did.

Object Creation

First off I wanted to create some basic examples of different ways of creating objects and assigning other objects as their prototypes.

Constructor Functions

This example makes use of constructor functions and the new operator.

Source

The previous sentence is basically a lie because the functions defined there are just normal functions. They’re not ‘constructor functions’, they’re just regular old functions. All of the magic that makes that code work is in the new operator.

When you invoke a function with new, an empty object is created and the function is bound to the object when it is called meaning that this now refers to the new object. If the function does not specifically return anything it returns this, which is the empty object. Calling new Entity() results in an object with the parameters x and y.

Output

For some reason it says everything is an Entity rather than the actual object that it is, but you can clearly see that the chain has been followed.

The result is an instance of Horse which has the species property set on it, and as the this in setName refers to the object that it is called against, the new instance of Horse has the property name as well.

Object.create

This example achieves the same result as the previous but uses Object.create instead.

Source

Object.create() returns an empty object with the object passed in to create as its prototype. This works just fine, but means that attaching properties to the new object is a bit tedious.

Output

This method differs from the first in that the resultant object only has the name property on it, whereas the above had species as well as name.

This is because in the first example horse is an instance, or a copy, of Horse. In this example, horse is an object that has a prototype of type Horse; rather than being an instance of Horse it has an instance of Horse as its parent. When horse.setName('Frank') is called it sets the name property on the newly-created Object rather than on its prototype.

Object.setPrototypeOf

I wanted to know if it were possible to define some regular classes and then build a prototype chain out of them.

Source

It turns out that you can, and that according to MDN you shouldn’t.

Output

Identical to the second example.

Classes

More for completeness than anything else, here’s how you do the same thing using ES2015 classes.

Source

Output

This is interesting as the final object has all of the properties set against it. Without doing any research into how ES2015 Classes work, I can only assume that when you instantiate a class the value of this is constant the entire way up through every extend call.

More

There are properties such as prototype.constructor that allow you to build your inheritance chain in different methods but that’s going beyond the scope of both this blog post and my own knowledge. I suggest to myself that I thoroughly read the MDN documentation on all of this and write a blog post about it, and I suggest this to you as well if you don’t know it!

Memory Dots

After familiarising myself with how to create objects with an inheritance chain I wanted to learn how to use Chrome to demonstrate that if you have a large amount of objects there is a reduction in memory use if you set a function on their shared parent rather than on the objects themselves. The result is MemoryDots! Have an 8.4mb gif:

MemoryDots creates and renders up to 200,000 jiggling squares in a canvas. Each square has an updatePosition function that updates the position of the square. In the gif you can see that there are two buttons: Create Simple and Create Inherit.

Simple Dots

Here’s the source of a Simple dot:

It’s a constructor function with the properties x, y and updatePosition. Each dot will have a copy of the entire updatePosition function on it.

Inherit Dots

Here’s the source of an Inherit dot:

The only difference is that updatePosition has been set on the object’s prototype rather than directly on the object itself. This means that each dot will now have a reference to the same instance of updatePosition.

Memory Consumption

Now let’s see how big the difference is between Simple and Inherit dots. If you have cloned MemoryDots the way I suggest that you follow along is to run yarn run build and then open up /dist/index.html directly in the browser. I was doing this inside webpack-dev-server and found that I was getting inconsistent results from webpack-dev-server doing things.

If you open the Chrome developer tools you’ll see a Memory tab. All of the memory consumption values below are from taking Heap snapshots.

The page loads with zero dots being rendered. In this case on my machine the Chrome Dev tools show that the page uses 4.4MB of ram.

I created 10,000 Simple dots and memory consumption increased to 6.1MB.

I cleared the dots again and memory consumption dropped back to 4.4MB. This is what I expected to happen but it’s nice to have it confirmed.

I then created 10,000 Inherit dots and memory consumption climbed to 4.9MB. That’s a 20% reduction in memory consumption from 10,000 simple dots. Nice.

Now for extra impact I did all that again but with 100,000 dots and the difference was huge. 100,000 Simple dots took up 21.4MB, and 100,000 Inherit dots used 9.2 MB. That’s a 57% reduction.

Here’s a screenshot of my Memory tab with those numbers on it that doesn’t really serve any purpose but since I took it already I’m putting it in this post:

Totally Unpredictable Results

So it turns out that if you duplicate code a lot it increases memory usage. Hardly a revelation, but it’s useful to be able to demonstrate this in a controlled environment and to understand exactly what is impacting your code’s footprint. We tend to develop an idea of what is and isn’t efficient code, but until you actually prove it you’re really just guessing. It’s also easy to dismiss a method of solving a problem as inefficient and never investigate the merits that it might actually have. Prove things with numbers.

MDN reference on new
MDN reference on Object.setPrototypeOf
MDN reference on Object.create
MDN reference on Class

Mozilla has some great resources on the Object API and tutorials on Object-Oriented Programming in JavaScript Inheritance in JavaScript.

The original article that prompted this post: Understand JavaScript Object Creation Patterns