I want to trigger JavaScript garbage collection. Is it possible? Why would I want to, or not want to, do this?
I went out on a small journey to seek an answer to one of your questions: Is it possible?
People all over town are saying that deleting the references will do the trick. Some people say that wiping the object is an extra guarantee (example). So I wrote a script that will try every trick in the book, and I was astonished to see that in Chrome (22.0.1229.79) and IE (9.0.8112.16421), garbage collection doesn't even seem to work. Firefox (15.0.1) managed without any major drawbacks apart from one (see case 4f down below).
In pseudo-code, the test goes something like this.
Create a container, an array, that will hold objects of some sort. We'll call this container Bertil here on.
Each and every object therein, as an element in Bertil, shall have his own array-container declared as a property. This array will hold a whole lot of bytes. We'll call any one of Bertil's elements, the object, Joshua. Each Joshua's byte array will be called Smith.
Here's a mind map for you to lean back on:
Bertil [Array of objects] -> Joshua [Object] -> Smith [Array of bytes] -> Unnamed [Bytes].
When we've made a mess out of our available memory, hang around for a sec or two and then execute any one of the following "destruction algorithms":
4a. Throw a delete operand on the main object container, Bertil.
4b. Throw a delete operand on each and every object in that container, kill every Joshua alive.
4c. Throw a delete operand on each and every array of bytes, the Smiths.
4d. Assign NULL to every Joshua.
4e. Assign UNDEFINED to every Joshua.
4f. Manually delete each and every byte that any Joshua holds.
4g. Do all of the above in a working order.
So what happened? In case 4a and 4b, no browser's garbage collector (GC) kicked in. In case 4c to 4e, Firefox did kick in and displayed some proof of concept. Memory was reclaimed shortly within the minute. With current hardcoded default values on some of the variables used as test configuration, case 4f and 4e caused Chrome to hang, so I can't draw any conclusions there. You are free to do your own testing with your own variables, links will be posted soon. IE survived case 4f and 4e but his GC was dead as usual. Unexpectedly, Firefox survived but didn't pass 4f. Firefox survived and passed 4g.
In all of the cases when a browser's GC failed to kick in, waiting around for at least 10 minutes didn't solve the problem. And reloading the entire page caused the memory footprint to double.
My conclusion is that I must have made a horrible error in the code or the answer to your question is: No we can't trigger the GC. Whenever we try to do so we will be punished severely and we should stick our heads in the sand. Please I encourage you to go ahead, try these test cases on your own. Have a look in the code were comment on the details. Also, download the page and rewrite the script and see if you can trigger the GC in a more proper way. I sure failed and I can't for the life of me believe that Chrome and IE doesn't have a working garbage collector.
http://martinandersson.com/dev/gc_test/?case=1
http://martinandersson.com/dev/gc_test/?case=2
http://martinandersson.com/dev/gc_test/?case=3
http://martinandersson.com/dev/gc_test/?case=4
http://martinandersson.com/dev/gc_test/?case=5
http://martinandersson.com/dev/gc_test/?case=6
http://martinandersson.com/dev/gc_test/?case=7
You can trigger manually JavaScript garbage collector in IE and Opera, but it's not recommended, so better don't use it at all. I give commands more just for information purpose.
Internet Explorer:
window.CollectGarbage()
Opera 7+:
window.opera.collect()
Garbage collection runs automatically. How and when it runs and actually frees up unreferenced objects is entirely implementation specific.
If you want something to get freed, you just need to clear any references to it from your javascript. The garbage collector will then free it.
If you explain why you even think you need to do this or want to do this and show us the relevant code, we might be able to help explain what your alternatives are.
Check your code for global variables. There may be data coming through an ajax call that is stored, and then referenced somewhere and you did not take this into account.
As a solution, you should wrap huge data processing into an anonymous function call and use inside this call only local variables to prevent referencing the data in a global scope.
Or you can assign to null all used global variables.
Also check out this question. Take a look at the third example in the answer. Your huge data object may still be referenced by async call closure.
This answer suggests the following garbage collection request code for Gecko based browsers:
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.garbageCollect();
Came across this question and decided to share with my recent findings.
I've looked to see a proper handling of a WeakMap in Chrome and it's actually looks okay:
1) var wm = new WeakMap()
2) var d = document.createElement('div')
3) wm.set(d, {})
at this stage weak map holds the entry cause d is still referencing the element
4) d = null
at this stage nothing references the element and it's weakly referenced object, and indeed after a couple of minutes entry disappeared and garbage collected.
when did the same but appended the element to the DOM, it was not reclaimed, which is correct, removed from the DOM and still waiting for it to be collected :)
Yes, you can trigger garbage collection by re-loading the page.
You might want to consider using a Factory Pattern to help re-use objects, which will greatly cut down on how many objects are created. Especially, if you are continuously creating objects that are the same.
If you need to read up on Factory Patterns then get yourself this book, "Pro Javascript Design Patterns" by Ross Harmes and Dustin Diaz and published by APress.
I was reading Trevor Prime's answer and it gave me a chuckle but then I realized he was on to something.
Reloading the page does 'garbage-collect'.
location.reload() or alternatives to refresh page.
JSON.parse/stringify and localStorage.getItem/setItem for persistence of needed data.
iframes as reloading pages for user experience.
All you need to do is run your code in an iframe and refresh the iframe page while saving useful information into localStorage. You'll have to make sure the iframe is on the same domain as main page to access its DOM.
You could do it without an iframe but user experience will no doubt suffer as the page will be visibly resetting.
If it is true that there is no way to trigger a GC, as implied by the other answers, then the solution would be for the appropriate browser standards group to add a new JavaScript function to window or document to do this. It is useful to allow the web page to trigger a GC at a time of its own choosing, so that animations and other high-priority operations (sound output?) will not be interrupted by a GC.
This might be another case of "we've always done it this way; don't rock the boat" syndrome.
ADDED:
MDN documents a function "Components.utils.schedulePreciseGC" that lets a page schedule a GC sometime in the future with a callback function that is called when the GC is complete. This may not exist in browsers other than Firefox; the documentation is unclear. This function might be usable prior to animations; it needs to be tested.
Related
I'm maintaining a JavaScript code where a function that does some heavy processing to generate arrays saves the results in a cache variable inside the function itself. It's implemented like this:
function heavyProcessingStuff(x) {
if(heavyProcessingStuff.cache == undefined)
heavyProcessingStuff.cache = [];
if(heavyProcessingStuff.cache[x] != undefined) {
return heavyProcessingStuff.cache[x]
} else {
return heavyProcessingStuff.cache[x] = x + 1
}
}
The weird thing is that there's a function executed when the page is unloaded that manually deletes every property of the cache variable, like this:
for (n in heavyProcessingStuff.cache) {
delete heavyProcessingStuff.cache[n]
}
I'm confused about why this was implemented this way.
Is this specific for some weird corner case? Is there any motivation to do so? Doesn't/shouldn't the browser garbage collect everything when the page closes?
Javascript uses garbage collection and it is best not to free memory explicitly. Good reading on this is in "De-referencing Misconceptions" in the article "Writing Fast, Memory-Efficient JavaScript".
A quote from the article:
It’s not possible to force garbage collection in JavaScript. You
wouldn’t want to do this, because the garbage collection process is
controlled by the runtime, and it generally knows best when things
should be cleaned up.
Without seeing the full context, there is one other possibility:
Earlier versions of IE (I know IE6 was susceptible), used a simple reference counting system for garbage collection. The trouble is that the DOM and JavaScript kept separate reference stacks.
This allowed for a situation to develop where the DOM (via an expando property) could hold onto a JavaScript reference -- and the JavaScript held onto a DOM reference.
This circular references would create a memory leak that lived on beyond the page, and the memory was only released when the page window was closed.
It was common to use the unload event to free memory and release any event handlers in an effort to avoid this.
In Chrome and Internet Explorer (or any browser that implements a process-per-tab model), you're right, there's absolutely no value. In fact, it's wasting CPU time to try and delete, since often when a tab is closed a process is killed, freeing up all non-shared resources.
For browsers that are a single process (Firefox still is, though progress is underway to change that), it's possible this could make the garbage collector do its work faster, if the reference counts get decremented to 0, but it's probably also pointless. Other than very old (IE6 for example) browsers, every modern garbage collector will probably sweep away everything on the next pass regardless of whether you explicitly remove references or not.
I am building large website with heavy JavaScript usage, all of my content is being loaded trough ajax, it is very similar to Facebook, and since there is a lot of different pages I need a lot of JavaScript, so what I thought of is to divide my script in to sections, each page would have it's own script file.
Now loading is simple, I just load a new file with each page, but my concern is what will happen if user goes trough 100 different pages and load 100 different script files?
At the moment my website doesn't have that many pages, but I'm quite sure it will grow to nearly 100 unique pages at some point in the future.
So what would happen to the user with slower computer? I'm guessing it would start to slow down a lot since there would be no refresh. From what I have read it is impossible to just unload all events and data from loaded script file in any easy way, and if I were to try that it might cost me a way to much time and effort to do that.
So my question would be, should I just leave it the way it is or try to do something about it? I am currently using jQuery with few plugins, and if I had to guess average file would be around 50-200 lines of code with mostly click events, and ajax calls.
Note, each page objects has it's own prefix for each class, for example: home_header, login_header
So there shouldn't be any conflicts between onClick event listeners and similar things.
Just because you are using AJAX doesn't automatically mean alarm bells with regards to memory usage... you should be more worried about the kind of things that cause memory leaks, and making sure you destruct as well as construct things properly:
http://javascript.crockford.com/memory/leak.html
http://nesj.net/blog/2012/04/javascript-memory-leaks/
http://www.ibm.com/developerworks/web/library/wa-memleak/
What is JavaScript garbage collection?
As a rule, in any large system I tend to create a helper constructor that keeps track of all the items I may wish to destroy at a later date or on page unload (event listeners, large attributes or object structures) all indexed by a namespace. Then when I've finished with a particular section or entity I ask the helper system - I call it GarbageMonkey :) - to clear a particular namespace.
For events it unbinds
For attributes it unsets
For arrays / objects it scans and unsets each key and can do so for sub elements too
For elements it removes and cleans content as much as possible
Obviously for the above to work you need to be wary about leaving variables lying around that can keep a reference to the data you hope to delete. So this means being aware of what garbage collection is, what closures are; and how between them they can keep a variable alive forever!! ..or at least until the browser/tab is destroyed. It also means using object structures rather than vars because you can delete keys in any scope that has access to the object, but you cannot do so for vars.
So do this:
var data = {}, methods = {}, events = {};
methods.aTestMethod = function(){
/// by assigning properties to an object, you can easily remove them later
data.value1 = 123;
data.value2 = 456;
data.value3 = 789;
}
Instead of this:
var value1, value2, value3;
var aTestMethod = function(){
value1 = 123;
value2 = 456;
value3 = 789;
}
The reason being because in the above you can later do this:
var i;
for( i in methods ){ delete methods[i]; }
for( i in data ){ delete data[i]; }
But you can't do this:
delete value1;
delete value2;
delete value3;
Now obviously the above wont protect you from a reference that points directly to a sub element of either methods or data. But if you only pass the methods and data objects around in your code, and keep tidy with regards to attaching methods as event listeners, then even if you do end up with a rogue reference it should only point to an empty object (after you've deleted it's contents that is).
If you recycle variables and don't pollute the global scope, you're on the right track; but as for your question, you should first find out if it is a practical concern.
This can be checked and monitored with a profiler - out-of-the box Chrome is pretty decent for it, just type about:memory in the URL and it'll give you a per-tab breakdown and would even let you compare memory usage between browsers. If you have some automated test scenarios set up (or are willing to navigate through 100 pages manually), such profiling will tell you if there's something majorly wrong with your website.
There is 2 different thing to take care of :
-memory usage
-memory leaks
For long-running webapps, memory leaks should be absolutely avoided, otherwise users will experience browser crashes. To monitor memory use, you can download process explorer :
http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx
Disable all your browser plugins, and then use your app, and perform repetitive tasks. If memory use raises, you got leaks. IE7 - IE8 do leak much more easily than modern browsers, and are much harder to debug, so it is useful to know what is you minim browser compatibility.
For memory usage, a few thing can help decrease the weight of your app :
instead of looping through dom elements and attaching event handlers functions, use event delegation. ED is really a golden gun here.
for IE 7 / 8 var nullification was necessary and I think it still helps for modern browsers(needs some testing). To do so, you need also to name your functions, so that you can remove them from memory. (see pebbl answers for more details on this)
keep control of the dom size. When nodes are added for a feature, they should also be removed when this feature is not used anymore.
add to all of your components some teardown() method that handles unloading.
ok, sorry I am a bit too quick here, but it would be nice to know :
what is your minimum browser
if you have detected leaks
if ED is a sufficient solution (often it is)
I've rolled my own javascript server side language called bondi. Just recently upgraded to the new spider monkey.
Now that JS enter local roots and leave local roots function is gone/useless from the 1.8.5 api, is it enough to just use anchor pointer(JS_AnchorPtr(varname)) at the end of your function calls to make sure the compiler isn't removing references to keep the garbage collector happy?
I've been testing it by removing all my references to JS_EnterLocalRootScope (see here)
/ Leave local root scope and adding JS_AnchorPtr() to the bottom of the script.
I looked up AnchorPoint function in the source code of spider monkey. Guess what... it does nothing. There's no doco for it either. I'm using it just so that I can get a mention in of those variables so the garbage collector doesn't kill them.
Well, blame seems to say that bug 519949 is recommending you use js::Anchor so that the conservative stack scanner will pick it up.
Note that the conservative scanner can find any GC thing that's on the stack or in registers, so the only really tricky case is where you use derived values when the "owning" GC thing may be dead, like so:
{
JSString *str = GetMeSomeStringYo();
const jschar *chars = str->chars();
// Note, |str| is not "live" here, but the derived |chars| is!
// The conservative stack scanner won't see |chars| and know
// to keep |str| alive, so we should be anchoring |str|.
DoSomethingThatCanCauseGC();
return chars[0];
}
If you're using C the JS_AnchorPtr at the end of the functions should be enough. You are correct that the function has a nop implementation! The idea is that, so long as it's performing a call to a shared object symbol with the variable to keep alive as a parameter, the calling function will have to keep that value around in machine state in order to perform the do-nothing call. This is more sucky for perf than js::Anchor.
There's one potential trap in the unlikely case that you're statically linking against SpiderMonkey and have Link Time Optimization enabled: the cross-object call may be inlined with a null implementation, eliminating liveness of the variable, in which case the same GC hazards may pop back up.
var Obj = function(){}; var X = new Obj();
will X = null properly clear memory?
Also would this be equivalent?
var Obj = function(){};
var X = {};
X.obj = new Obj();
delete(X.obj);
EDIT
It would seem that although deleting X.obj would NOT immediately clear memory, it would help the garbage collection. If I don't delete X.obj, there would still be a pointer to an object and so the GC may not clean it up.
Although I'm picking #delnan's answer, if you're reading this you should def also catch Benubird's article.
I also notice I accidentally wrote delete(X) originally instead of delete(X.obj) - sorry.
The short answer is that you don't. delete simply removes a reference (and not in the way you try to use it, see the above link - delete is one of those language features few people actually understand), nothing more. The implementation clears memory for you, but it's not your business when (and even if, strictly speaking - this is why one shouldn't rely on finalizers in GC'd languages that offer them) it does. Note though:
Only objects that can be proven to be unreachable (i.e. no way to access it) to all code can be removed. What keeps references to whom is usually fairly obvious, as least conceptually. You just need to watch out when dealing with lots of closures, as they may capture more variables than you think. Also note that circular references are cleaned up properly.
There's a bug in old (but sadly still used) IE versions involving garbage collection of JS event handlers and DOM elements. Google (perhaps even SO) should have better material on my memory.
On the plus side, that means you won't get dangling pointer bugs or (save of course the aforementioned pitfalls) memory leaks.
No, that will not clear memory.
Read this:
http://perfectionkills.com/understanding-delete/
No - Javascript runs GC when it feels like it.
The Delete method only deletes the reference - not the object. Any other references would be left out in the open waiting for the garbage collector.
JavaScript has its own GC, and it will run around and clean things up when nothing refers to them anymore.
I still think it's a good practice to null objects.
Deleteing an object also helps the GC because it will see something dangling, and say "I'm going to eat you because you're all alone (and now some cynical laugh)".
You should look at Deleting Objects in JavaScript
Even though there's a GC, you still want to ensure your script is optimized for performance as peoples computers, browsers, and fricken toolbars (and the number of them), will vary.
Generally speaking, memory management in Javascript is user-agent-specific. The basics of the garbage collector are through reference-counting. So, by setting a reference to null (using the delete keyword or by explicit assignment), you can assure yourself that a reference will be cleaned up, IF the object does not have any references that will live outside of its creation scope. That being the case, the GC will have already cleaned up any objects or variables whose scope has ended without your explicitly setting it to null.
There are some things to take care of, though - circular references are easy to create in JS, especially between a DOM element and an object. Care must be taken to clear (or not create in the first place) references to and/or from DOM elements within objects. If you do create a to/from reference related to DOM, be sure to explicitly clean them up by setting the references to null - both on your object and on the DOM element. Simply setting a parent object to null is not sufficient if there are child objects with references to/from DOM or localStorage because those references will live on, and if there was any reference from the child to the parent, then the parent will live on in memory because of that reference.
Web pages can actually leak trash in your memory this way - after you navigate away, the circular references keep objects and DOM elements in memory until you've restarted the browser!
An article on the subject: http://docstore.mik.ua/orelly/webprog/jscript/ch11_03.htm, and another detailed look: http://blogs.msdn.com/b/ericlippert/archive/2003/09/17/53038.aspx
JavaScript memory is generally handled similarly to Java - I mean there is (or there should be) a garbage collector which would delete the object if there is no references to it. So yes, simply "nullifying " the reference is the only way you should "handle" freeing memory, and the real freeing is the JS host part.
I am given with a project that my seniors worked on, this project is developed keeping in mind that the whole page should never be reloaded. So obviously everything went into ajax. And it has 100's of lines that go something like this.
function iCreateProblems()
{
//some rubbish
document.getElementById('some_rubbish').innerHTML=obj1.responseText;
}
and a typical response text is "<div onClick="handleMe()">some thing stupid</div>";
There are three main div's on the page and everything is repeatedly loaded and reloaded into these div's. Now as far as i understand, this will clearly give away to memory leak, right? So how do i fix this? There are about 8000 lines of code that go in this manner. Is there a way i can fix the memory leak? There are hundreds of handlers that are assigned like this. What do i do now?
No, it shouldn't. You do not handle memory directly in js; garbage collectors remove everthing unneeded and you don't need to explicitly delete on divs' contents if you overwrite the content.
Without seeing the rest of the code there's no way to tell. Despite what people are saying here, it's very easy to create a memory leak in JS and not notice it. The way JS garbage collectors usually work is by marking variables that are "reachable". Variables become considered "unreachable" when their "mark" is taken off and not reapplied, (which is to say, they're completely unreachable by any of the programmer's functions or scopes). After the mark is removed, they're eventually cleaned up by the garbage collector.
One contrived example of a memory leak would be:
(function() {
var xhr = $.get('http://google.com/');
$('a').click(function() {
console.log('hello');
});
})();
As long as that element exists on the page, with that event listener bound to it, the variable xhr will never be cleaned up by the garbage collector. Even though xhr is never used anywhere in your code, the garbage collector will refuse to clean it up because it is still "reachable" by your event listener.
edit: I should also mention, there is also the possibility of a memory leak occurring from bugs in a poorly written JS engine. (See also: Internet Explorer). Most DOM libraries account for this and make sure to avoid these problems, but it can still happen. For these memory leaks you need to find a workaround for that particular engine.