How to get the size of a JavaScript object?

Viewed 360931

I want to know the size occupied by a JavaScript object.

Take the following function:

function Marks(){
  this.maxMarks = 100;
}

function Student(){
  this.firstName = "firstName";
  this.lastName = "lastName";
  this.marks = new Marks();
}

Now I instantiate the student:

var stud = new Student();

so that I can do stuff like

stud.firstName = "new Firstname";

alert(stud.firstName);

stud.marks.maxMarks = 200;

etc.

Now, the stud object will occupy some size in memory. It has some data and more objects.

How do I find out how much memory the stud object occupies? Something like a sizeof() in JavaScript? It would be really awesome if I could find it out in a single function call like sizeof(stud).

I’ve been searching the Internet for months—couldn’t find it (asked in a couple of forums—no replies).

23 Answers

This is a hacky method, but i tried it twice with different numbers and it seems to be consistent.

What you can do is to try and allocate a huge number of objects, like one or two million objects of the kind you want. Put the objects in an array to prevent the garbage collector from releasing them (note that this will add a slight memory overhead because of the array, but i hope this shouldn't matter and besides if you are going to worry about objects being in memory, you store them somewhere). Add an alert before and after the allocation and in each alert check how much memory the Firefox process is taking. Before you open the page with the test, make sure you have a fresh Firefox instance. Open the page, note the memory usage after the "before" alert is shown. Close the alert, wait for the memory to be allocated. Subtract the new memory from the older and divide it by the amount of allocations. Example:

function Marks()
{
  this.maxMarks = 100;
}

function Student()
{
  this.firstName = "firstName";
  this.lastName = "lastName";
  this.marks = new Marks();
}

var manyObjects = new Array();
alert('before');
for (var i=0; i<2000000; i++)
    manyObjects[i] = new Student();
alert('after');

I tried this in my computer and the process had 48352K of memory when the "before" alert was shown. After the allocation, Firefox had 440236K of memory. For 2million allocations, this is about 200 bytes for each object.

I tried it again with 1million allocations and the result was similar: 196 bytes per object (i suppose the extra data in 2mill was used for Array).

So, here is a hacky method that might help you. JavaScript doesn't provide a "sizeof" method for a reason: each JavaScript implementaion is different. In Google Chrome for example the same page uses about 66 bytes for each object (judging from the task manager at least).

i want to know if my memory reduction efforts actually help in reducing memory

Following up on this comment, here's what you should do: Try to produce a memory problem - Write code that creates all these objects and graudally increase the upper limit until you ran into a problem (Browser crash, Browser freeze or an Out-Of-memory error). Ideally you should repeat this experiment with different browsers and different operating system.

Now there are two options: option 1 - You didn't succeed in producing the memory problem. Hence, you are worrying for nothing. You don't have a memory issue and your program is fine.

option 2- you did get a memory problem. Now ask yourself whether the limit at which the problem occurred is reasonable (in other words: is it likely that this amount of objects will be created at normal use of your code). If the answer is 'No' then you're fine. Otherwise you now know how many objects your code can create. Rework the algorithm such that it does not breach this limit.

Building upon the already compact solution from @Dan, here's a self-contained function version of it. Variable names are reduced to single letters for those who just want it to be as compact as possible at the expense of context.

const ns = {};
ns.sizeof = function(v) {
  let f = ns.sizeof, //this needs to match the name of the function itself, since arguments.callee.name is defunct
    o = {
      "undefined": () => 0,
      "boolean": () => 4,
      "number": () => 8,
      "string": i => 2 * i.length,
      "object": i => !i ? 0 : Object
        .keys(i)
        .reduce((t, k) => f(k) + f(i[k]) + t, 0)
    };
  return o[typeof v](v);
};

ns.undef;
ns.bool = true;
ns.num = 1;
ns.string = "Hello";
ns.obj = {
  first_name: 'John',
  last_name: 'Doe',
  born: new Date(1980, 1, 1),
  favorite_foods: ['Pizza', 'Salad', 'Indian', 'Sushi'],
  can_juggle: true
};

console.log(ns.sizeof(ns.undef));
console.log(ns.sizeof(ns.bool));
console.log(ns.sizeof(ns.num));
console.log(ns.sizeof(ns.string));
console.log(ns.sizeof(ns.obj));
console.log(ns.sizeof(ns.obj.favorite_foods));

I had problems with the above answer with an ArrayBuffer. After checking the documentation, I found that ArrayBuffer has a byteLength property which tells me exactly what I need, hence:

function sizeOf(data)
{
    if (typeof(data) === 'object')
    {
        if (data instanceof ArrayBuffer)
        {
            return data.byteLength;
        }
        // other objects goes here
    }
    // non-object cases goes here
}

console.log(sizeOf(new ArrayBuffer(15))); // 15

Reference:

The accepted answer does not work with Map, Set, WeakMap and other iterable objects. (The package object-sizeof, mentioned in other answer, has the same problem).

Here's my fix

export function roughSizeOfObject(object) {
  const objectList = [];
  const stack = [object];
  const bytes = [0];
  while (stack.length) {
    const value = stack.pop();
    if (value == null) bytes[0] += 4;
    else if (typeof value === 'boolean') bytes[0] += 4;
    else if (typeof value === 'string') bytes[0] += value.length * 2;
    else if (typeof value === 'number') bytes[0] += 8;
    else if (typeof value === 'object' && objectList.indexOf(value) === -1) {
      objectList.push(value);
      if (typeof value.byteLength === 'number') bytes[0] += value.byteLength;
      else if (value[Symbol.iterator]) {
        // eslint-disable-next-line no-restricted-syntax
        for (const v of value) stack.push(v);
      } else {
        Object.keys(value).forEach(k => { 
           bytes[0] += k.length * 2; stack.push(value[k]);
        });
      }
    }
  }
  return bytes[0];
}

It also includes some other minor improvements: counts keys storage and works with ArrayBuffer.

Take a Head snapshot in chrome, and monitor the sizes of your objects: enter image description here

enter image description here

Related