I was reading a blog in smashing magazine by Addy Osmani memory efficient JS in the closure section. I understand the following function holds reference to largeStr and GC can't claim it.
var a = function () {
var largeStr = new Array(1000000).join('x');
return function () {
return largeStr;
};
}();
And the solution he mentioned here don't hold a reference to largeStr and GC can claim it. Instead he uses smallStr.
var a = function () {
var smallStr = 'x';
var largeStr = new Array(1000000).join('x');
return function (n) {
return smallStr;
};
}();
I got Addy's point of not holding reference to a large thing. However, I was wondering is there any (better) way, I could have the functionality of first function and make it memory efficient.
I was reading a blog in smashing magazine by Addy Osmani memory efficient JS in the closure section. I understand the following function holds reference to largeStr and GC can't claim it.
var a = function () {
var largeStr = new Array(1000000).join('x');
return function () {
return largeStr;
};
}();
And the solution he mentioned here don't hold a reference to largeStr and GC can claim it. Instead he uses smallStr.
var a = function () {
var smallStr = 'x';
var largeStr = new Array(1000000).join('x');
return function (n) {
return smallStr;
};
}();
I got Addy's point of not holding reference to a large thing. However, I was wondering is there any (better) way, I could have the functionality of first function and make it memory efficient.
Share Improve this question edited Oct 26, 2019 at 11:28 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Oct 21, 2013 at 21:32 Jhankar MahbubJhankar Mahbub 9,84810 gold badges50 silver badges52 bronze badges 3- The article did not intend to imply that the two functions do the same thing. They're just examples. But you could move the code to create the big string into the returned function, I guess. It'd create a new one every time though. – Pointy Commented Oct 21, 2013 at 21:36
- creating same straing again and again is not a good idea. one global would be better (save processing time)... actually i was explaining this and wondering what would be the best solution for this. – Jhankar Mahbub Commented Oct 21, 2013 at 21:41
- Well it depends on what happens to the string. If the code needs the string only rarely, and only for a short time, it's probably better to create a new copy each time. If the code needs it frequently, then the first version is probably OK (and better than a global variable). – Pointy Commented Oct 21, 2013 at 21:43
2 Answers
Reset to default 9The first function creates largeStr
and returns a function that references it. So it is logical that the garbage collector can't free up largeStr
because it is still in use by the function that is now contained in the a
variable.
The second function has no lasting reference to largeStr
so the garbage collector can free it.
It sounds like you're asking if there is a way to keep a reference to something large, yet not use that memory. That answer to that is No.
Also, these are not technically "leaks" at all. They are legitimate memory usage.
You could have the functionality of the first function without the memory usage by not prebuilding the large string. If you built it upon demand, then it would consume no memory until someone called the function. That is obviously a direct tradeoff between execution speed and memory usage, but that's the choice you get here. If it's pre-cached, then it consumes memory. If it's built only upon demand, then it doesn't consume memory until used.
Here's the built upon demand version that doesn't consume memory until used:
var a = function () {
return function () {
return new Array(1000000).join('x');
};
}();
which doesn't have to be written this obtusely. It can also just be this since there is no closure involved:
var a = function() {
return new Array(1000000).join('x');
}
These two versions have the disadvantage that they creates the string each time a()
is called, but the advantage that nothing is ever permanently cached. When all uses of a()
are done, everything is garbage collected.
Or, cache it only when it is first used:
var a = function () {
var largeStr;
return function () {
if (!largeStr) {
largeStr = new Array(1000000).join('x');
}
return largeStr;
};
}();
This has the advantage that no memory is consumed until a()
is first called and subsequent calls to a()
don't have to recreate the large string, but the single largeStr will never be garbage collected after it is created.
Which is best depends upon your usage pattern and which tradeoff is more important in your design/use.
You need very deliberate setup to cause the V8 closure memory leak:
function create() {
var a = "x";
var b = new Array(1000000).join('x');
//Force context-allocation for b
(function(){b;});
return function() {
return a;
};
}
window.a = create();
b
cannot be accessed by the code but it cannot be collected until you pletely get rid of window.a
:
http://jsfiddle/bwPVe/
If you do heap snapshot in chrome you will see.