JavaScript empty object size
Asked Answered
M

2

11

What is the memory footprint of an empty object in JavaScript? If object is created using object literal syntax:

let emptyObj = {};

in Google Chrome Developer Tools (Profiles tab), after taking snapshot, it shows that Shallow Size as well as Retained Size is equal to 56 Bytes. Also, the same size is present if object is created by:

let emptyObj = Object.create(null);

For me, that's far too much, as I am creating a lot of objects (not necessarily empty, but mostly with only few properties) during code execution and I have to store them in memory. I am assuming that if it would be possible to decrease empty-object size, it would be also possible to decrease size of an object with properties by the same amount of Bytes.

For example, if object looks like this:

let foo = {bar: 4};

and it has the size of, let's say, 56 (empty object overhead) + 6 (key) + 8 (value) = 70 Bytes, then reducing size of an empty object by 40 Bytes would result foo having size of 30 Bytes (16 + 6 + 8).

Is this correct interpretation of Chrome's empty object size? Is it possible to decrease it? Would it result in decreasing size of not-empty object?

Meteoritics answered 8/10, 2016 at 12:10 Comment(13)
"Is it possible to decrease it?" No. You have no control over to how the browser interprets the "create an object" operation and what it actually stores in your computer's memory to represent that. In general simply stop worrying about object sizes in JS, pages and web apps use literally hundreds if not thousands of them. Just don't forget to remove references to objects you no longer need and GC will do the rest.Antisepsis
The language specification (ECMA-262) does not define implementation, so the memory footprint likely differs between implementations. Creating an object with an object literal should return exactly the same result as creating it using the Object constructor.Barbiebarbieri
@MitchKarajohn Unfortunately, I need to store millions of them and I cannot reduce that amount. So I try to figure out how to decrease the size of "base" of every object. That would give me huge memory gain.Meteoritics
@Meteoritics just store them and see how your performance is affected. The optimisations you can make from there are among the lines of whether you should be really storing them all in memory or can you be creating them gradually, and to make sure you are making use of the GC by removing references to unused objects. Again, in general, don't worry about memory too much, unless you have really created a memory hog. I would worry more if I had to render the information contained in these millions of objects for some reason.Antisepsis
Did your objects have all the same properties, and all props are primitive types (int, float, boolean)?Knowles
@MitchKarajoh So, basically I store copy of the state of an application (very frequently) and I cannot remove any object from past that represents that state. Thus, this objects cannot be garbage-collected if I want to return to that state.Meteoritics
Oh, yes, also what @Knowles said: Check out how JS stores to variables. Things are not as bad as you might have imagined.Antisepsis
@Knowles Mostly primitives, copied (not references), so things are bad :(Meteoritics
What you described that you are doing reminded me of something like imutable states. Things are not so bad as you imagine, due to how JS stores to variables. Take a look in this article. Start reading from the "Consider your app state as a tree, with a root and child nodes." part, it's releveant to your case I think. Also see this graphic, in case it helps you visualise this.Antisepsis
@MitchKarajohn Sadly, this also does not apply, since I am not developing web application with my own state and state-modification flow, but rather recording state that is modified by game engine.Meteoritics
Maybe not so bad. It's can be a bit painfull to implement but you could store all your objects, flatten in a single array, are even better, preallocated ArrayBuffer. A bit like C structs arrays are layed out when malloced.Knowles
@Meteoritics How about saving only diffs instead of the full state, and every like 100 entries you store a full state. Depending on how much your state is changing per iteration, this could reduce the memory footprint tremendously, and due to the "keyframes" you can recreate every single state in reasonable time.Drupelet
@Drupelet Yes, I thought about it and I will definitely use this approach, but I also wanted to optimize it on even earlier stage (before diffing) if it's possible. But, it seems that it isn't.Meteoritics
M
1

It is not possible to decrease the size of an empty object.

An ordinary object like {}:

  • Has overhead that is created by the language implementation, and cannot be managed by ECMAScript language directly.
  • Includes essential internal slots: [[PrivateElements]], [[ProtoType]], [[Extensible]]. See OrdinaryObjectCreate in the ECMA Script specification.
  • Is associated with essential internal methods: [[GetPrototypeOf]], [[SetPrototypeOf]], [[IsExtensible]], [[PreventExtensions]], [[GetOwnProperty]], [[DefineOwnProperty]], [[HasProperty]], [[Get]], [[Set]], [[Delete]], [[OwnPropertyKeys]]. See Ordinary Object Internal Methods and Internal Slots in the ECMA Script specification.

Note that internal slots and method references are not object properties; they cannot be directly accessed via the ECMA Script language. How they actually are implemented is implementation dependent. As stated by the ECMA Script specification for "ordinary object":

...The exact manner in which this is accomplished is determined by the implementation. [...] internal slots are allocated as part of the process of creating an object and may not be dynamically added to an object. [...] the ECMAScript language provides no direct way to associate internal slots with an object.

The above list of internal slots and internal method associations has 3+11=14 entries. If we imagine an ECMA Script compliant implementation where these values take up 4 bytes each, we arrive at 56 bytes for the minimal footprint of an ECMA Script object.

Maintaining a history of state

From comments it seems your specific context is about maintaining a history of state, so to be able to return to a previous state.

There are a few things you can consider:

  • Instead of storing a history of immutable objects, you could define do/undo methods on a single mutable object, only saving the difference. This will add some overhead to the processing. For instance, a chess implementation could store moves and the current board instead of all the boards that were "visited" during a game.

  • If the object properties are primitives, you could maintain one array for each property, where the index in these arrays selects the values that belong together. So you would actually store the set of objects in a transposed format. The memory footprint could be further reduced by using typed arrays. This is however an antipattern from the viewpoint of OOP.

  • Some data might be stored more efficiently in bitmaps. If these happen to be larger than 32 bits, you could use a BigInt data type, which has support for bit operators.

Mcgovern answered 29/5, 2024 at 6:53 Comment(0)
M
-2

The empty object is not so empty. If you do emptyObj.__proto__ = null it will delete the __proto__ chain. But then it may throw errors for some basic methods of Object. However simple property assignments and property reads will work well. Hope it would save you some bytes.

Machree answered 9/10, 2016 at 12:33 Comment(10)
__proto__ is not an own property of an object. You cannot delete it. If you don't understand how it works, please don't use it. And it's deprecated anyway. Use Object.setPrototypeOf/Object.getPrototypeOf instead.Punitive
If you want a really empty object, just instantiate it using Object.create(null). Don't mess with the prototype chain of existing objects.Punitive
In any case, there's hardly a memory difference in object size between the [[prototype]] link pointing to Object.prototype and the [[prototype]] link pointing to null.Punitive
Talent does what he knows, genius does what he wants. You CAN delete prototype chain in this particular case, to decrease amount of memory used. However if you delete __proto__, you cannot use some internal methods of object and you must know the consequences of this.Machree
@Punitive You'll never be a good Programmer if you don't dare to go around taboos and break the common rules.Machree
My point is knowing what you do not want. To start with, "delete prototype chain to decrease amount of memory" is totally wrong - even the OP measured that {} and Object.create(null) take the same amount of memory. And no, whether an object has a __proto__ property or not has nothing to do with using "internal" methods (I assume you mean Object.prototype ones). You can delete Object.prototype.__proto__ and everything will still work.Punitive
Oh, I'm regularly breaking rules that are considered "common" because I know where they apply specifically and where they don't, or when a taboo is the only working solution. But in any case, one must point out the rules when someone who does not know what he is doing breaks them. And suggesting to break them in an SO answer for a clueless OP is never a good idea.Punitive
@Machree As Bergi mentioned, even after creating object without prototype payload it seems to have the same size in Google Chrome as object literal ({}). So setting __proto__ to null won't help here.Meteoritics
The key word here is 'it seems'. Have you measured it?Machree
Creating object without prototype (I don't know what Bergi had in mind) and setting __proto__ to null are different things. Check it with profiler.Machree

© 2022 - 2025 — McMap. All rights reserved.