Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does {} consume less memory than [] for nested objects in Javascript?

I had some legacy JS code that creates a giant nested object structure with []. The code goes some like this

var data = [];
data ["first"] = [];
data ["first"]["second"] = [];
data ["first"]["second2"] = "hello";

It is about 250+ KB of javascript, which is fairly large. When I try to wrap it around with requirejs to load into another requirejs module, it throws Out Of Memory error.

The error goes away if I use {} where I was using [].

I did some homework on [] vs. {} over the weekend and the cause seems to be that using associated arrays as nested dictionaries may be leaky in Javascript since array extends a JS Object and may have more update stuff going on when appending new objects into it. But does it explain the memory consumption issue? Or it is related to how Requirejs parse a module's object?

I don't have enough knowledge about doing JS memory instrumentation and make comparisons between using {} or [] in browser engines, so it is hard to reach conclusions. Any hint or suggestion on how to instrument {} vs. [] would be welcome.

Update: I tried some sizeOf() via node yesterday. I used all of the existing ones: "js-sizeof", "object-sizeof", "sizeof"

Code:

var sizeof = require('object-sizeof');


var obj = [];
obj['ball'] = 'hello';
obj['air'] = 'hello';
obj['ball']['fire'] = 'world';
obj['ball']['ice'] = [];
console.log(sizeof(obj));

var obj2 = {};
obj2['ball'] = 'hello';
obj2['air'] = 'hello';
obj2['ball']['fire'] = 'world';
obj2['ball']['ice'] = [];
console.log(sizeof(obj2));

The results is

[]: 34 {}: 34

The sizeOf is actually the same., but maybe something else happened with [] that could trigger out of memory issue. I am not sure if it is the requirejs parsing it that trigger it or some V8 optimization path. I don't think the Lint tools even suggest against this practice so it is rather ambiguous which way is the "right" way in practice

like image 388
ttback Avatar asked Jun 02 '15 15:06

ttback


People also ask

Are objects faster than arrays?

Objects will be used when we need fast access, insertion and removal of an element as objects are comparatively much faster than the arrays in case of insertion and removal.

How do I use less memory in JavaScript?

Hence there is no explicit way to allocate or free up memory in JavaScript. Just initializing objects allocates memory for them. When the variable goes out of scope, it is automatically garbage collected(frees up memory taken by that object.)

How are JavaScript objects stored in memory?

The heap is a different space for storing data where JavaScript stores objects and functions. Unlike the stack, the engine doesn't allocate a fixed amount of memory for these objects. Instead, more space will be allocated as needed. Allocating memory this way is also called dynamic memory allocation.

How are arrays stored in memory JavaScript?

In Javascript, an array is a Hashtable Object type so the interpreter doesn't need to keep track of physical memory and changing the value of an element doesn't affect other elements as they're not stored in a contiguous block of memory.


1 Answers

There is no such thing as an "associated array" in JavaScript. [ 1, 2, 3 ] is array literal syntax; it initializes an Array. { foo: "bar" } is object literal syntax; it initializes an Object. A quirk of JavaScript, however, is that Arrays also happen to be Objects, which is why this code "works":

var data = [];
data["first"] = [];
data["first"]["second"] = [];

...but you shouldn't do it, because it doesn't make any sense. You're initializing an empty Array ([]), but then you're not using it like an Array—you're using it like an Object. If you're using property names (data["first"], which is equivalent to data.first) instead of integer keys (data[0]), then you want to use an Object. There is no scenario in which you should initialize an Array when you're going to use it like an Object.

As a rule of thumb, if you need each item to have a name, or need to be able to access them quickly by a name, use an Object ({}) and use strings for keys. If you need to be able to iterate over the items in order, use an Array with integers for keys.

I don't know the exact cause of your out-of-memory error—especially not without seeing your actual code—but it is definitely the case that you should be using an Object ({}), not an Array ([]) when you're not using integer keys. JavaScript engines optimize everything they can, and Arrays and Objects are no exception, so it's not surprising that when you use an Array in a way that the engine doesn't expect it might cause performance or memory problems.

P.S. As a matter of style, consider using property notation (or "dot notation," i.e. foo.bar) instead of subscript notation (i.e. foo["bar"]) when dealing with Objects:

var data = {};
data.first = {};
data.first.second = {};
data.first.second2 = "hello";

This is exactly equivalent to the code you posted, but it's easier to read and might help you remember that Objects and Arrays have different uses. You could also just express this as a single object literal:

var data = {
  first: {
    second: {},
    second2: "hello"
  }
};

This is also exactly equivalent and helps you see the "structure" of your object (as long as you're disciplined about indentation).

Most JavaScript style guides say that you should always use "dot notation" unless you have keys that would cause a syntax error. For example, if you have a property named "foo/bar", you obviously can't do this:

var obj.foo/bar = 1;

...because it's a syntax error. So you have to do this:

var obj["foo/bar"] = 1;

...which is perfectly valid. These cases tend to be the exception, so I would encourage to always use dot notation unless you have to use subscript notation.

like image 136
Jordan Running Avatar answered Sep 21 '22 15:09

Jordan Running