On V8:
Boolean, number, string, null and void 0 literals take constant 4/8 bytes of memory to for the pointer or the immediate integer value embedded in the pointer. But there is no heap allocation for these at all as a string literal will just be internalized. Exception can be big integers or doubles which are boxed with 4/8 bytes for the box pointer and 12-16 bytes for the box. In optimized code local doubles can stay unboxed in registers or stack, or an array that always contains exclusively doubles will store them unboxed.
Consider the meat of the generated code for:
function weird(d) {
var a = "foo";
var b = "bar";
var c = "quz";
if( d ) {
sideEffects(a, b, c);
}
}
As you can see, the pointers to the strings are hard-coded and no allocation happens.
Object identities at minimum take 12/24 bytes for plain object, 16/32 bytes for array and 32/72 for function (+ ~30/60 bytes if context object needs to be allocated). You can only get away without heap allocation here if you run bleeding edge v8 and the identity doesn't escape into a function that cannot be inlined.
So for example:
function arr() {
return [1,2,3]
}
The backing array for values 1,2,3 will be shared as a copy-on-write array by all arrays returned by the function but still unique identity object for each array needed to be allocated. See how complicated the generated code is. So even with this optimization, if you don't need unique identities for the arrays, just returning an array from upper scope will avoid allocation for the identity everytime the function is called:
var a = [1,2,3];
function arr() {
return a;
}
Much simpler.
If you have memory problems with js without doing anything seemingly crazy, you are surely creating functions dynamically. Hoist all functions to a level where they don't need to be recreated. As you can see from above, merely the identity for a function is very fat already considering most code can get away with static functions by taking advantage of this
.
So if you want to take anything from this, avoid non-IIFE closures if your goal is performance. Any benchmark that shows they are not a problem is a broken benchmark.
You might have intuition that what does additional memory usage matter when you have 8GB. Well it wouldn't matter in C. But in Javascript the memory doesn't just sit there, it is being traced by garbage collector. The more memory and objects that sits there, the worse performance.
Just consider running something like:
var l = 1024 * 1024 * 2
var a = new Array(l);
for( var i = 0, len = a.length; i < len; ++i ) {
a[i] = function(){};
}
With --trace_gc --trace_gc_verbose --print_cumulative_gc_stat
. Just look how much work was done for nothing.
Compare with static function:
var l = 1024 * 1024 * 2
var a = new Array(l);
var fn = function(){};
for( var i = 0, len = a.length; i < len; ++i ) {
a[i] = fn;
}