I'm not running into a memory leak in my application yet, but I'm worried about possible problems in the future. I would like to know if doing something like this:
SomeClass.prototype.someMethod= function() {
var that= this
this.$div2.click(function() {
that.someMethod2();
});
}
And lets say that this.$div2 is appended to another div this.$div1. If I call
this.$div1.remove();
and later loses the reference of my SomeClass instance does the SomeClass instance gets garbage collected? And what about the HTML element this.$div2? this.$div2 would not be inside the DOM because it is appended to this.$div1.
I ask this because the event handler in this.$div2 might keep a reference to the HTML element this.$div2 and also keeps a reference to the instance of SomeClass through the closure because of the variable "that".
So should I care about properly removing all events and HTML elements like this? Or simply removing the "root" element (this.$div1) solves the problem?
this.$div2 is appended to this.$div1. If I call this.$div1.remove(); and later lose the reference of my SomeClass instance does the SomeClass instance gets garbage collected?
Yes, when all references to it are lost - also those through event handlers , - the instance can get garbage-collected.
And what about the HTML element this.$div2? this.$div2 would not be inside the DOM because it is appended to this.$div1.
It does not matter whether it is currently attached to the DOM. If some non-collectible object references $div1, it also could access its child node $div2 and that one's event handlers, so the instance referenced from the handler would not be collectible.
I ask this because the event handler in this.$div2 might keep a reference to the HTML element this.$div2 and also keeps a reference to the instance of SomeClass through the closure because of the variable "that".
That's a circular reference and should get handled well by the engines (when none of the objects inside the circle is referenced from outside it can get collected). However, (old?) Internet Explorers fail to do this when a DOM object is involved in the circle.
For that reason the .remove jQuery method (code) internally calls the (internal) cleanData method which detaches all event listeners.
So should I care about properly removing all events and HTML elements like this? Or simply removing the "root" element (this.$div1) solves the problem?
Yes, calling remove on a jQuery wrapper automatically removes all events (from all child elements) and DOM nodes.
Should I care about properly removing all events and HTML elements
like this?
The short answer is No! at least in 99% of the cases, it will not matter in any way because the memory used by one DOM element is trivial compared to the overall memory used by a web page.
However it is always a good practice to release the memory used by disposing unneeded objects, but you cannot say that GC would definitely releases the memory utilized by the elements because garbage collection is entirely up to the browser! In theory GC should only kick in when there are no references to the DOM element, at least that's how Chrome works, but in languages like JavaScript, you don't explicitly tell the run-time you're done with the object, things get messy in JavaScript so quickly: a function might pass the object on to some more functions, the object might get saved away as a member within yet another object, an object might get referenced through the closure etc, so it's completely up to the browser how and what to collect!
In your case removing div1 frees the html document and the element would not render in the view, in fact jQuery's remove method takes care of removing all the events, expando properties, and child elements attached to the element together with the element itself, however you keep a reference of div1 and div2 in yet another object making both DOM elements Orphan elements! removing SomeClass instance variable releases all references to the DOM elements making them candidate for garbage collection but here comes the tricky that variable that causes the DOM element make a reference to the instance of SomeClass through clusure! This issue is known as Circular Reference in IE:
JavaScript Objects and DOM elements that store references to one
another cause Internet Explorer’s garbage collector to not reclaim
memory, resulting in memory leaks
You can read more about it here
This particular leak is mostly of historical interest IE<8, but a good example of breaking circular links is to avoid using the variable that, instead use proxy or delegate to change the event handler's context to some particular context.
ECMA 5th bind method is quit useful changing contexts when in comes to DOM event handlers, here's a simple handler based on your code without using variable closure:
this.$div2.click((function() {
this.someMethod2();
}).bind(this));
If you will create element dynamically, then assign to them events. i think that your code is not a good way to do that. you should follow this manner:
for fixed elements if you need an event, use these two functions; the first called in the constructor, the second in the destructor.
on_Events: function() {
$('your_form').on('event_name', {element_Selector}, callback_function)
},
off_Events: function() {
$('your_form').off('event_name', {element_Selector}, callback_function)
}
for dynamically objects. add events when creating an element and remove these events just before destroying the element.
Related
I tried to understand how 'addEventListener' works like when we add event, where does it save? DOM tree? Or certain memory place?. But couldn't find answer, all the posts I've ever seen said "You can't", because there's no standard for it and every browser has different implementation.
So I was trying to skip it, but got a question about event delegation. In many posts, people say event delegation saves memory, because we don't have to attach event listener to each components. But I thought, I have to understand how addEventListener works to understand why event delegation saves memory.
So what's the reason event delegation is memory efficient?
Somewhere in the browser's memory, there's a data structure that contains the list of event listeners for each element. If you call addEventListener() separately for 100 different elements, it will create 100 entries in this table.
But if you use event delegation, you only have to call addEventListener() once, so there's only 1 entry in the table.
You can actually see a representation of this data by opening the Event Listeners tab in the Elements panel of Developer Tools.
However, the amount of memory you save is probably not very significant. Each listener is probably just a couple of pointers, one to something representing the event type (click, change, etc.) and another to the callback function. If all 100 event listeners call the same function, there's just one function object and 100 pointers to it. If the function is a closure, there will also be an environment object containing the variables it closes over, which will add a little more memory use, but not very much.
On the other hand, when you use delegation, the callback function needs to do extra work to determine if the event target is an appropriate nested element. This makes it a little slower. It will also be called if the event is triggered on an element that's in the container element but not one of the elements you're delegated to (and will run repeatedly as the event bubbles out), so the function is run more often. If memory were really at a premium, this would be a classic time/space tradeoff.
In practice, delegation isn't used to save memory, it's used to simplify the design. It's most often used when you're adding elements to the DOM dynamically, or changing attributes (e.g. class names) that the event binding depends on. Delegation allows you to define the event listener once, rather than having to add or remove it from elements as they're added or modified.
I am newbie to jQuery. I am bit confused whether is it fine or may cause memory leak ?
Here is the code: This method is called on certain date filters for each new values
function preapreTooltip(chart) {
var tickLength = chart.xAxis[0].tickPositions.length,
ticks = chart.xAxis[0].ticks,
tickPositions = chart.xAxis[0].tickPositions;
for ( var iCntr = 0; iCntr < tickLength; iCntr++) {
var tickVal = tickPositions[iCntr];
//.label or .mark or both
(function(tickVal) { // Is this good practice to call function like this?
ticks[tickVal].label
.on('mouseover', function(event) { // Is this good practice to call function like this?
var label = '', labelCnt=0;
$(chart.series).each(function(nCntr, series) {
//business logic for each series
});
// calling method to show values in a popup
});
ticks[tickVal].label.on('mouseout', function(event) { // Is this good practice to call function like this?
try {
hideWrapper(); // hides popup
} catch (e) {
// do nothing
}
});
})(tickVal);
}
}
Whilst there are browser specific issues that need to be avoided when writing large pure JavaScript projects, when using a library such as jQuery it should be assumed that the library's design helps you avoid these problems. However, considering memory leaks are rather hard to track down, and each different version of a particular browser could behave differently - it is far better to know how to generally avoid memory leaks than being specific:
If your code is being iterated many times, make sure the variables you are using can be discarded by garbage collection, and are not tied up in closure references.
If your code is dealing with large data structures, make sure you have a way of removing or nullifying the data.
If your code constructs many objects, functions and event listeners - it is always best to include some deconstructive code too.
Try to avoid attaching javascript objects or functions to elements directly as an attribute - i.e. element.onclick = function(){}.
If in doubt, always tidy up when your code is finished.
You seem to believe that it is the way of calling a function that will have an effect on leaking, however it is always much more likely to be the content of those functions that could cause a problem.
With your code above, my only suggestions would be:
Whenever using event listeners try and find a way to reuse functions rather than creating one per element. This can be achieved by using event delegation (trapping the event on an ancestor/parent and delegating the reaction to the event.target), or coding a singular general function to deal with your elements in a relative way, most often relative to this or $(this).
When needing to create many event handlers, it is usually best to store those event listeners as named functions so you can remove them again when you are finished. This would mean avoiding using anonymous functions as you are doing. However, if you know that it is only your code dealing with the DOM, you can fallback to using $(elements).unbind('click') to remove all click handlers (anonymous or not) applied using jQuery to the selected elements. If you do use this latter method however, it is definitely better to use jQuery's event namespacing ability - so that you know you are only removing your events. i.e. $(elements).unbind('click.my_app');. This obviously means you do have to bind the events using $(elements).bind('click.my_app', function(){...});
being more specific:
auto calling an anonymous function
(function(){
/*
running an anonymous function this way will never cause a memory
leak because memory leaks (at least the ones we have control over)
require a variable reference getting caught in memory with the
JavaScript runtime still believing that the variable is in use,
when it isn't - meaning that it never gets garbage collected.
This construction has nothing to reference it, and so will be
forgotten the second it has been evaluated.
*/
})();
adding an anonymous event listener with jQuery:
var really_large_variable = {/*Imagine lots of data here*/};
$(element).click(function(){
/*
Whilst I will admit not having investigated to see how jQuery
handles its event listeners onunload, I doubt if it is auto-
matically unbinding them. This is because for most code they
wont cause a problem, especially if only a few are in use. For
larger projects though it is a good idea to create some beforeunload
or unload handlers that delete data and unbind any event handling.
The reason for this is not to protect against the reference of the
function itself, but to make sure the references the function keeps
alive are removed. This is all down to how JS scope works, if you
have never read up on JavaScript scope... I suggest you do so.
As an example however, this anonymous function has access to the
`really_large_variable` above - and will prevent any garbage collection
system from deleting the data contained in `really_large_variable`
even if this function or any other code never makes use of it.
When the page unloads you would hope that the browser would be able
to know to clear the memory involved, but you can't be 100% certain
it will *(especially the likes of IE6/7)* - so it is always best
to either make sure you set the contents of `really_large_variable` to null
or make sure you remove your references to your closures/event listeners.
*/
});
tearDowns and deconstruction
I've focused - with regard to my explanations - on when the page is no longer required and the user is navigating away. However the above becomes even more relevant in today's world of ajaxed content and highly dynamic interfaces; GUIs that are constantly creating and trashing elements.
If you are creating a dynamic javascript app, I cannot stress how important it is to have constructors with .tearDown or .deconstruct methods that are executed when the code is no longer required. These should step through large custom object constructs and nullify their content, as well as removing event listeners and elements that have been dynamically created and are no longer of use. You should also use jQuery's empty method before replacing an element's content - this can be better explained in their words:
http://api.jquery.com/empty/
To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.
If you want to remove elements without destroying their data or event handlers (so they can be re-added later), use .detach() instead.
Not only does coding with tearDown methods force you to do so more tidily (i.e. making sure you to keep related code, events and elements namespaced together), it generally means you build code in a more modular fashion; which is obviously far better for future-proofing your app, for read-ability, and for anyone else who may take over your project at a later date.
Here is an excellent article to detect memory leak using Chrome or Safari : http://javascript.crockford.com/memory/leak.html
It is use not well known functionalities of Developer Tools panel.
Interesting and very useful!
EDIT
It was not the good link (but still useful). Here is the one : http://gent.ilcore.com/2011/08/finding-memory-leaks.html
I'm working on a completely ajax web project where a section content is always generated through DOM manipulation or using jQuery's load function. I had been using "live" but am very interested in moving away from "live" and using "on" for performance benefits. When a new page loads a whole new set of bindings required for that section also need to get loaded. The html sections have some parent DOMs (basically wrappers for different content areas of the web page) that never change allowing me to do bindings on them for all future DOM elements that will be created on the page.
In terms of memory and performance trade off which is generally the better way to handle event bindings?
After a new section has finished loading its html, bind all the events needed for that specific page instance on DOM elements that will be removed when a page changes.
Bind every event on the very first page load to DOM elements (not to the document though like live does) that are known to always exist.
Memory issues with listeners can usually be dealt with fairly easily (don't hold large chunks of data in closures, don't create circular references, use delegation, etc.).
"Live" just uses delegation (as far as I know) - you can implement delegation quite simply without it using simple criteria, e.g. class or id, with listeners on the unchanging parent elements. Delegation is a good strategy where it replaces numerous other listeners, the content is constantly being changed and identifying elements that should call functions is simple.
If you follow a strategy of attaching numerous new listeners every time content changes, you also have to detach the old ones when they are replaced as a strategy to reduce the likelihood of memory leaks. Performance (in terms of time taken to attach and remove listeners as part of the DOM update) usually isn't that much of an issue unless you are doing hundreds of them.
With delegation, a parent element listens for events, checks if the event.target/srcElement is one it cares about for that event, then calls the appropriate function perhaps using call to set the value of this if required.
Note that you can also simply include inline listeners in the inserted HTML, then you never need to worry about memory leaks, delegation or adding and removing listeners. Inline listeners using a simple function call are no more complex than adding any other attribute (class, id, whatever) and require zero extra programming on the client. I don't think inline listeners were ever an issue for memory leaks.
Of course the "unobtrusive javascript" mob will howl, but they are very practical, functional and robust, not to mention supported by every browser that ever supported javascript.
Given the following code
<div id="app">
<div id="foo" />
</div>
<script>
$('#foo').bind('click', function(){});
</script>
I plan on replacing the contents of #app [e.g. $('#app').html('...');, or innerHTML = '...';]. I know that I can use jQuery's .remove() which calls a 'destroy' handler that unbinds events. The fact that there is a destroy handler set up to remove events leads me to believe that without unbinding the events, when the DOM element is removed, the handler will still exist in memory.
So, if the DOM element #foo no longer exists, does the handler disappear as well, or does it get lost in browser memory?
jQuery keeps track of event handlers itself, which is part of why you need to use unbind (nowadays it's off) if you're removing the element from the DOM not through a jQuery method (if you use jQuery's empty or remove, as you mentioned it handles this itself inernally). This is so jQuery knows it can release its reference to the handler.
If it weren't for that, then in theory, you wouldn't have to do anything because once the DOM element is removed from memory, it's no longer reachable, and so in theory shouldn't keep the event handler in memory. That's the theory. The reality is very different, it can be very easy to end up with a situation (particularly on IE) where neither the DOM element nor the event handler can get cleaned up because they're each causing the other to stick around — a memory leak. JavaScript has no issue with circular references (it understands them and can release two things that are pointing to each other as long as nothing else is pointing to them), but the DOM part of the browser is likely to be written in a different language with a different garbage collection mechanism (IE uses COM, which uses reference counting rather than reachability). jQuery helps you avoid this pitfall with IE (part of why it keeps track of event handlers), but you have to use unbind (nowadays off) (or remove elements via empty, remove, etc.) as a consequence.
The take away message: As you hook, so shall you unhook. :-) (And/or use jQuery when removing elements, since it will handle this.)
Somewhat related: If you're adding and removing elements a lot, you might look to see if event delegation (which jQuery makes really easy via the delegation signatures for on) might help.
Just happened to read the docs on jQuery's empty() method:
To avoid memory leaks, jQuery removes
other constructs such as data and
event handlers from the child elements
before removing the elements
themselves.
If I bind an event to a DOM element, does the event ever get destroyed if the element does? By destroying of an element I'm referring to removeChild(). A move of the node to another location with appendChild() leaves the event listener untouched.
Basically I'm interested in this because I want to know if I need to do some cleanup/tear down.
I think you mean event handler here, right? If so then it is a valid concern for garbage collection purposes to be careful with functions attached to elements via "onfoo" attributes. IE has what amounts to separate garbage collectors for the DOM and for JavaScript, and they don't know much about each other.
I believe what suffices is to make sure that "onfoo" attributes are set to null when DOM elements are tossed aside. By so doing, the JavaScript code will have broken the reference to JavaScript memory allocated for the handlers, so the DOM garbage collector won't leak. Of course this goes for any other random attributes you may have added to DOM elements too.
Though I hate to suggest using a JavaScript framework for questions not so tagged, and in fact I won't actually make such a suggestion here, but I will say that one of the things frameworks (usually) do for you is try to keep the DOM "clean" in this kind of situation.