It's about garbage collection and memory leaks in different browsers.
In general, this topic has already been discussed a lot, I want to consider one interesting case of closures.
To begin with, in the implementation of closures in C #, only those objects that are actually used in the internal function are referenced, and not all visible variables. I was interested to compare the behavior of C # and javascript. Since there are a lot of js implementations and they are all different, I decided to test how it is implemented in popular browsers.
Test will be: IE, Chrome, FF and Opera. OS: win7 32bit.
')
So let's start with just allocating a large piece of memory:
Test1 (memory allocation):
function test1() { var buff = []; for (var i = 0; i < sz; i++) buff.push(i); alert(1); var buff2 = []; for (var i = 0; i < sz; i++) buff2.push(sz - i); alert(2); }
I measure memory by a private working set, this is of course not quite correct, but itβs good for tracking changes.
changes in consumed memory (in KB) are in the order:
was β first alert β second alert (in parentheses - changes)
if the changes are minor, then I will mark it (~ 0)
IE8: sz = 2e6;
Let's start with IE8, this is a rather slow browser, so for it we take the size of the array (sz) - 2 million
memory: 9036 β 87476 β 167880 (+78440, +80404)
Let's wait a bit (suddenly the garbage collector will work), but the memory does not decrease.
let's try again: 167868 β 248260 β 328640 (+80392, +80380)
again a similar picture. I repeated this procedure until IE ate 1GB of memory. I left him alone for half an hour (and suddenly, after all, he cleans up the garbage), but nothing has changed.
Conclusion : IE8 will not take out the garbage until you close the tab / press F5.
Chrome 8.0.552.237): sz = 10e6;
Next comes chrome (maybe not the latest version, but I donβt expect strong changes)
memory: 5072 β 78480 β 143624 (+73408, +78480)
Waiting for: 143676 (~ 0)
run the test again: 143676 β 72636 β 112604 (-71040, +39968)
waiting again: 112608 (~ 0)
third launch: 112608 β 96092 β 148704 (-16516, +52612)
waiting for: 148700 (~ 0)
waiting for a long time (5 minutes): 5356 (-143344)
Conclusion : Chrome takes out the garbage either when the allocated memory reaches a certain limit, or by itself, after a rather long interval of time.
Firefox 3.6.13: sz = 20e6;
Ognelis consumed not a lot of memory, so even larger array size is used for it:
memory: 39064 β 124968 β 211180 (+85904, +86212)
wait: 211164 (~ 0)
try2: 211164 β 297480 β 383592 (+86316, +86112)
wait: 211280 (-172312)
wait: 38908 (-172372)
Conclusion : Firefox collects trash faster than chrome, but takes some time to do this (~ 1 min).
Opera 11.00 (build 1156): sz = 10e6;
memory: 86436 β 349244 β 612096 (+262808, +262852)
wait: 87736 (-524360)
try2: 87736 β 349928 β 612100 (+262192, +262172)
wait: 87748 (-524352)
Opera consumed a lot more memory than other adequate browsers (more - only IE), but did it almost instantly. The memory was also freed almost immediately.
The final result for all browsers:
IE8 - slow, allocates ~ 40 bytes for each number
Chrome8 - average speed, allocates ~ 8 bytes per number
FF3.6 - average speed, allocates ~ 4.4 bytes per number
Opera11 - high speed, allocates ~ 27 bytes for each number (with sz = 10e6)
and ~ 16 bytes with sz = 1e6
Test2 (explicit memory release):
let's try to force IE to say that we no longer need this memory
IE8: sz = 2e6;
try length = 0:
memory: 10912 β 89352 β 169760 β 169760 β 169760
try delete buff:
memory: 12848 β 89356 β 169752 β 169740 β 169740
try splice:
memory: 12972 β 89464 β 169852 β 250368 β 330760,
a copy seems to be created here, but the old version of the array is not cleared
try pop:
memory: 6776 β 85192 β 165508 β 165508 β 165508
Result : a complete failure. IE8 garbage collection is a myth.
Obviously, we will only test the garbage collection when using closures on the three remaining browsers.
Test3 (which objects get into the circuit):
function test3() { var buff = []; for (var i = 0; i < sz; i++) buff.push(i); alert(1); var buff2 = []; for (var i = 0; i < sz; i++) buff2.push(sz - i); alert(2); return function(x) { alert(buff.length); } }
Chrome 8: sz = 10e6;
memory: 4796 β 72720 β 139956 β β 45232
The β β symbol hereafter will mean waiting for garbage collection
2nd try: 8924 β 88000 β 135100 β β 135092
3rd try: 135092 β 151036 β 174128 βaper 46092
Result : only referenced are taken into the closure, GC works as expected, but sooo long.
Firefox 3.6: sz = 20e6;
memory: 39864 β 124812 β 210752 ββ¬ 124560
2nd try: 124560 β 210772 β 296928 β β 124544
Result : only referenced are taken to the closure, GC works as expected.
Opera 11: sz = 10e6;
memory: 102072 β 364268 β 626448 βaper 364760
2nd try: 364760 β 626940 β 889108 β β 364752
Result : judging by the memory consumed, only referenced is taken into the closure, GC works as expected; However, this is not the case at all ...
Now let's slightly complicate the task with the js engine: insert inside the closure of eval ()
Test4 (short circuit with eval inside):
function test4() { var buff = []; for (var i = 0; i < sz; i++) buff.push(i); alert(1); var buff2 = []; for (var i = 0; i < sz; i++) buff2.push(sz - i); alert(2); return function(x) { alert(buff[0]); eval(x); } } function z() { document.getElementById("div1").x("alert(buff2[0])"); }
<div onclick="this.x = test4()" id="div1">test4()</div> <div onclick="z()">z()</div>
It is obvious that now all variables should be saved in the closure: is there anything that will come to us in eval?
Chrome:
Reliable result for chromium is hard to get, because a very long time to wait for garbage collection.
But judging by the allocated memory and the fact that the z () test works, we can conclude that everything works as expected.
Firefox 3.6: sz = 20e6;
memory: 39300 β 125696 β 210732 β β 210256
2nd try: 210256 β 298340 β 383496 β β 211112
Result : a closure with eval refers to all declared variables. As expected.
Opera 11: sz = 10e6;
memory: 102076 β 364272 β 626456 β Members 364764
judging by the memory, the eval in the closure is ignored, however, the test z () with eval works correctly.
Quite unexpected behavior. Where does the array buff2 come from, if the memory has already been freed from it? Can opera somehow optimize the storage of large arrays?
Let's see what happens if we just return an array:
Test5:
function test5() { var buff = []; for (var i = 0; i < sz; i++) buff.push(i); alert(1); var buff2 = []; for (var i = 0; i < sz; i++) buff2.push(sz - i); alert(2); return { a: buff, b: buff2 };
Opera:
2 objects: 41764 β 304324 β 567088 β Members 304556
1 object: 41896 β 304348 β 567108 β Members 173240
It seems that opera really somehow packs huge arrays after filling them out.
But this means that in the 4th and 3rd tests, it references all the variables from the scope.
So, InternetExplorer even version 8 works terribly with memory.
Firefox and Chrome are very economical with large arrays (with which they can be congratulated) and it is logical to optimize closures.
Opera takes all visible variables into the closure, which is not bad in principle, but you should always keep in mind and do not create scripts on the pages of the site that allocate arrays of 10 million numbers.
PS:
chepetsk.ru
:
ibm.com/developerworks/web/library/wa-memleak/
blogs.msdn.com/b/oldnewthing/archive/2006/08/02/686456.aspx