Memory allocation and DOM elements - javascript

While reading an MDN article about Memory Management (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management) I came upon the following question: Is it right to say that
var e = document.getElementById('div_id');
allocates a DOM element? I am using the same words with MDN article. In my opinion this is not right since the element-object with id="div_id" is already created in memory as part of the DOM tree. So var e, just references it and does not allocate new memory. Am I right or not?
Thank you

document.getElementById('div_id') will not allocate memory for the element, as it already exists in the document tree.
However, the article you have linked doesn't actually say that memory would be allocated. So I don't see anything wrong with the article.
The only memory that gets allocated is for the variable e of the expression var e = document.getElementById('div_id');. This allocation happens in the stack. A new reference to the element is assigned to e, which consumes memory (even though it's a very small amount). No new memory allocation happens in the heap.

Yes, document.createElement('div/any tag'); will create a new node and hence it has to allocate memory for it.
I guess, the memory management in the document is talking about particular case as explained below. Suppose you create a DOM element
var myDom = document.createElement('dom'); //or consider array of dom objects
//attach dom objects to document using appendChild/insertBefore apis
//on some **event** you remove dom nodes from the document using removeChild api
But at last, if you forget that the myDom/array object you used to store DOM nodes is not freed up, the DOM objects will still exists in the memory, even though they are not attached to the docuemnt.
So, if you think the DOM is not used any more, assign myDom = null, this will make GC to free the space.
Doc : Release when the memory is not needed anymore
Most of memory management issues come at this phase. The hardest task
here is to find when "the allocated memory is not needed any longer".
It often requires for the developer to determine where in the program
such piece of memory is not needed anymore and free it.

Related

How to know if element created with document.createElement still exists

I'm creating an anchor element using document.createElement in my PhantomJS tests like this:
var mockLink = null;
beforeEach(function() {
mockLink = document.createElement('a');
});
it('should foo', function() {
// Use mockLink in some way
});
I want to clean up the element in the afterEach code so that each test will have a newly-created instance of it. Unfortunately, element.remove() isn't supported in PhantomJS. Source
afterEach(function() {
mockLink.remove(); // Error!
});
And I want to do this without adding the webpage plugin.
removeChild is supported by PhantomJS, but my newly-created element doesn't have a parent:
mockLink.parentElement // undefined
mockLink.parentElement.removeChild(mockLink); // doesn't work
mockLink.parentNode // also undefined
My newly-created link also doesn't seem to be on the document at all.
mockLink.href = 'www.google.com';
document.getElementsByTagName('a'); // doesn't contain google.com
So I can't do
document.removeChild(mockLink);
mockLink.ownerDocument.contains(mockLink) also returns false
It seems like there are no references to mockLink stored anywhere besides my mockLink variable, meaning that I can just set mockLink equal to null if I want to let that memory be freed up by garbage collection. But how do I verify this works? If I set any other variable to mockLink and then console.log it, it will still come out as defined since it will be a new reference to the same space in memory. How do I verify that mockLink is really being deleted when I set the variable to null?
You've found yourself a conundrum.
If you kill all references to the element by setting mockLink = null and making sure there are no other references to the element, then the garbage collector should be able to free that object.
But, you cannot verify that because in order to verify that the element is no longer available, you'd have to keep a reference to it, but that would prevent it from getting garbage collected in the first place. Thus, the conundrum.
This is an issue that can't really be measured directly from Javascript itself.
If you want to design a one-time test to make sure the memory is being reclaimed, then you can take a memory snapshot, create a several hundred thousand objects stored in an array, clear the array, wait a short time for GC to run, take another memory snapshot, repeat the same process several more times and verify that memory usage of the process is not steadily increasing or study the memory snapshot to make sure none of those objects appear in the memory footprint.
If your javascript environment supports weak references then you can create a weak reference to the node. Depending on the implementation notification mechanisms such as callbacks or reference queues that tell you when it has been collected may also be available.
Unprivileged javascript contexts in web browsers currently don't offer such an API. Node.js, Nashorn and browser addon environments do.

Finding JavaScript memory leaks with Chrome

I've created a very simple test case that creates a Backbone view, attaches a handler to an event, and instantiates a user-defined class. I believe that by clicking the "Remove" button in this sample, everything will be cleaned up and there should be no memory leaks.
A jsfiddle for the code is here: http://jsfiddle.net/4QhR2/
// scope everything to a function
function main() {
function MyWrapper() {
this.element = null;
}
MyWrapper.prototype.set = function(elem) {
this.element = elem;
}
MyWrapper.prototype.get = function() {
return this.element;
}
var MyView = Backbone.View.extend({
tagName : "div",
id : "view",
events : {
"click #button" : "onButton",
},
initialize : function(options) {
// done for demo purposes only, should be using templates
this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";
this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
},
render : function() {
this.$el.html(this.html_text);
this.wrapper = new MyWrapper();
this.wrapper.set(this.$("#textbox"));
this.wrapper.get().val("placeholder");
return this;
},
onButton : function() {
// assume this gets .remove() called on subviews (if they existed)
this.trigger("cleanup");
this.remove();
}
});
var view = new MyView();
$("#content").append(view.render().el);
}
main();
However, I am unclear how to use Google Chrome's profiler to verify that this is, in fact, the case. There are a gazillion things that show up on the heap profiler snapshot, and I have no idea how to decode what's good/bad. The tutorials I've seen on it so far either just tell me to "use the snapshot profiler" or give me a hugely detailed manifesto on how the entire profiler works. Is it possible to just use the profiler as a tool, or do I really have to understand how the whole thing was engineered?
EDIT: Tutorials like these:
Gmail memory leak fixing
Using DevTools
Are representative of some of the stronger material out there, from what I've seen. However, beyond introducing the concept of the 3 Snapshot Technique, I find they offer very little in terms of practical knowledge (for a beginner like me). The 'Using DevTools' tutorial doesn't work through a real example, so its vague and general conceptual description of things aren't overly helpful. As for the 'Gmail' example:
So you found a leak. Now what?
Examine the retaining path of leaked objects in the lower half of the Profiles panel
If the allocation site cannot be easily inferred (i.e. event listeners):
Instrument the constructor of the retaining object via the JS console to save the stack trace for allocations
Using Closure? Enable the appropriate existing flag (i.e. goog.events.Listener.ENABLE_MONITORING) to set the creationStack property during construction
I find myself more confused after reading that, not less. And, again, it's just telling me to do things, not how to do them. From my perspective, all of the information out there is either too vague or would only make sense to someone who already understood the process.
Some of these more specific issues have been raised in #Jonathan Naguin's answer below.
A good workflow to find memory leaks is the three snapshot technique, first used by Loreena Lee and the Gmail team to solve some of their memory problems. The steps are, in general:
Take a heap snapshot.
Do stuff.
Take another heap snapshot.
Repeat the same stuff.
Take another heap snapshot.
Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
For your example, I have adapted the code to show this process (you can find it here) delaying the creation of the Backbone View until the click event of the Start button. Now:
Run the HTML (saved locally of using this address) and take a snapshot.
Click Start to create the view.
Take another snapshot.
Click remove.
Take another snapshot.
Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
Now you are ready to find memory leaks!
You will notice nodes of a few different colors. Red nodes do not have direct references from Javascript to them, but are alive because they are part of a detached DOM tree. There may be a node in the tree referenced from Javascript (maybe as a closure or variable) but is coincidentally preventing the entire DOM tree from being garbage collected.
Yellow nodes however do have direct references from Javascript. Look for yellow nodes in the same detached DOM tree to locate references from your Javascript. There should be a chain of properties leading from the DOM window to the element.
In your particular you can see a HTML Div element marked as red. If you expand the element you will see that is referenced by a "cache" function.
Select the row and in your console type $0, you will see the actual function and location:
>$0
function cache( key, value ) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
if ( keys.push( key += " " ) > Expr.cacheLength ) {
// Only keep the most recent entries
delete cache[ keys.shift() ];
}
return (cache[ key ] = value);
} jquery-2.0.2.js:1166
This is where your element is being referenced. Unfortunally there is not much you can do, it is a internal mechanism from jQuery. But, just for testing purpose, go the function and change the method to:
function cache( key, value ) {
return value;
}
Now if you repeat the process you will not see any red node :)
Documentation:
Eliminating memory leaks in Gmail.
Easing JavaScript Memory Profiling In Chrome DevTools.
Here's a tip on memory profiling of a jsfiddle: Use the following URL to isolate your jsfiddle result, it removes all of the jsfiddle framework and loads only your result.
http://jsfiddle.net/4QhR2/show/
I was never able to figure out how to use the Timeline and Profiler to track down memory leaks, until I read the following documentation. After reading the section entitled 'Object allocation tracker' I was able to use the 'Record Heap Allocations' tool, and track some some Detached DOM nodes.
I fixed the problem by switching from jQuery event binding, to using Backbone event delegation. It's my understanding that newer versions of Backbone will automatically unbind the events for you if you call View.remove(). Execute some of the demos yourself, they are set up with memory leaks for you to identify. Feel free to ask questions here if you still don't get it after studying this documentation.
https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling
Basically you need to look at the number of objects inside your heap snapshot. If the number of objects increases between two snapshots and you've disposed of objects then you have a memory leak. My advice is to look for event handlers in your code which do not get detached.
There is an introduction video from Google, which will be very helpful to find JavaScript memory leaks.
https://www.youtube.com/watch?v=L3ugr9BJqIs
You also might want to read :
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
It explains the use of the chrome developer tools and gives some step-by-step advices on how to confirm and locate a memory leak using heap snapshot comparison and the different hep snapshot views available.
You could also look at the Timeline tab in developer tools. Record the usage of your app and keep an eye on the DOM Node and Event listener count.
If the memory graph would indeed indicate a memory leak, then you can use the profiler to figure out what is leaking.
A couple of important notes in regards to identifying memory leaks using Chrome Developer tools:
1) Chrome itself has memory leaks for certain elements such as password and number fields. https://bugs.chromium.org/p/chromium/issues/detail?id=967438. Avoid using those while debugging as they polute your heap snapshot when searching for detached elements.
2) Avoid logging anything to the browser console. Chrome will not garbage collect objects written to the console, hence affecting your result. You can suppress output by placing the following code in the beginning of you script/page:
console.log = function() {};
console.warn = console.log;
console.error = console.log;
3) Use heap snapshots and search for "detach" to identify detached DOM elements. By hovering objects, you get access to all the properties including id and outerHTML which may help identify each element.
If the detached elements are still too generic to recognize, assign them unique IDs using the browser console prior to running your test, e.g.:
var divs = document.querySelectorAll("div");
for (var i = 0 ; i < divs.length ; i++)
{
divs[i].id = divs[i].id || "AutoId_" + i;
}
divs = null; // Free memory
Now, when you identify a detached element with, lets say id="AutoId_49", reload your page, execute the snippet above again, and find the element with id="AutoId_49" using the DOM inspector or document.querySelector(..). Naturally this only works if your page content is predictable.
How I run my tests to identify memory leaks
1) Load page (with console output suppressed!)
2) Do stuff on page that could result in memory leaks
3) Use Developer Tools to take a heap snapshot and search for "detach"
4) Hover elements to identify them from their id or outerHTML properties
I second the advice to take a heap snapshot, they're excellent for detecting memory leaks, chrome does an excellent job of snapshotting.
In my research project for my degree I was building an interactive web application that had to generate a lot of data built up in 'layers', many of these layers would be 'deleted' in the UI but for some reason the memory wasn't being deallocated, using the snapshot tool I was able to determine that JQuery had been keeping a reference on the object (the source was when I was trying to trigger a .load() event which kept the reference despite going out of scope). Having this information at hand single-handedly saved my project, it's a highly useful tool when you're using other people's libraries and you have this issue of lingering references stopping the GC from doing its job.
EDIT:
It's also useful to plan ahead what actions you're going to perform to minimize time spent snapshotting, hypothesize what could be causing the problem and test each scenario out, making snapshots before and after.
Adding my 2 cents here with the tools available in 2021: https://yonatankra.com/how-to-profile-javascript-performance-in-the-browser/
There's a short video version here: https://yonatankra.com/detect-memory-leak-with-chrome-dev-tools

JQuery Remove and memory leaks

Im working on a game, and ive seen a lot of memory consuption, im using jquery animate, and after the animation is done, i .remove() the element, my question is, after removing an element from the dom tree, the object still exist in memory?
Javascript is a garbage collected language. That means that an object in memory will be released when no code holds any references to it and (for a DOM object) it's not in the DOM. So, when you remove an object from the DOM, as long as no other part of your javascript has a reference to that DOM object, the DOM object will be cleaned up and it's memory returned to the available memory pool when the garbage collector gets a chance to run.
Keep in mind that when memory is freed by the garbage collector, it may not be returned to the system right away or ever. It may stay as memory allocated to the browser, but it will be available for use by other memory requests within the browser. So, freeing memory in your script won't necessarily make the total memory used by the browser go down.
It is only a memory leak if repeatedly carrying out the same operation over and over causes the total memory used by the browser to continually rise. Only then can you be sure that some memory is being permanently consumed by a "leak".
There are a number of nuances about garbage collection, particularly for older versions of IE, but for modern browsers, mostly what you need to keep in mind is that if you hold a reference to an object in your own javascript data structures, it will not be garbage collected. If you don't hold a reference to it and it's not in the DOM, it will be freed and its memory recycled.
If there are no references to the element, garbage collection will clean it up on its next run. You're just fine using .remove, but don't bother worrying about the garbage collection.

Can jQuery.data cause a memory leak?

Would the following piece of code create a memory leak.
According to the jQuery documentation use of the data function avoids memory leaks. It would be useful to confirm whether the following is safe.
var MyClass = function(el) {
// Store reference of element in object.
this.element = $(el);
};
// Store reference of object in element.
$('#something').data('obj', new MyClass('#something'));
Obviously the code as it stands would take up extra memory as long as the DOM element is still connected to the DOM. But I'm guessing you're asking whether it would continue using extra memory after the DOM element is no longer in use.
Update: Thanks to Joey's answer (which he has since deleted), I spent some time reading up on memory leaks in javascript, and it appears my assumptions in the paragraph below are incorrect. Because DOM elements don't use pure garbage collection, a circular reference like this would normally prevent both the DOM element and the javascript object from ever being released. However, I believe the remainder of this answer is still correct.
Without a deep knowledge of how javascript engines implement garbage collection, I can't speak authoritatively on the topic. However, my general understanding of garbage collection makes me think that your code would be "safe" in the sense that after the #something element is removed from the DOM, the resulting MyClass object would only have a reference to an object that has no other connections. The graph algorithms of the garbage collector should be able to identify that the DOM element and its MyClass object are "floating in space" and unconnected to everything else.
Furthermore, jQuery goes out of its way to strip data and events that are associated with a given DOM element once it is removed from the DOM. From the documentation:
jQuery ensures that the data is removed when DOM elements are removed via jQuery methods, and when the user leaves the page.
So assuming you use jQuery consistently, you would only have a one-way reference once the object is removed from the DOM anyway, which makes it that much easier possible for the garbage collector to know it can get rid of these objects.
So as long as you don't have something else referencing the MyClass object once the DOM element is removed, you shouldn't have a memory leak.
I suppose it depends on the Javascritp engine.
You have have the question precisely enought to perform a test. I added a long string in the object and ran the potential leak in a large loop.
As a result, I don't think in leaks in IE8 nor in Chrome.
But I could not reproduce these leakeage patterns either.
This can lead to a memory leak.
the theory of jQuery.data method may use A Data inner class to cache data for the dom element.
of course,when you remove the cache data,jQuery will unreference the data.
but the inner cache is a increasing array,when you you it ,it will go upon.
so ,in the end,there will be very big cache array,which will lead memeory leak.
In a long run web app,this may leak memory crash.
The data attribute only stores string values.

Do I have to clean custom properties (Expandos) on window.onunload event?

In one article I have seen that it may be good to clear all expandos on window.unload event to prevent memory leaks.
I cannot understand why to do this.
Isn't the browser cleaning all the DOM and its relevant resources of it once you leave the page anyway?
Thanks,
burak ozdogan
Hey, great question. The problem is with circular references between JavaScript objects and DOM nodes.
Let's say you have a global JavaScript object which points to a DOM node and the node has an expando property back to the object. When the page unloads, the script engine "nulls-out" the JavaScript object so it no longer points to the DOM node. But, it cannot release the object from memory because there is still a reference to it (from the DOM). Then the script engine terminates.
Expando properties on the DOM are nothing but references to other objects. When the DOM is being cleaned up, it breaks those references but assumes that the objects are still being used. In this example, the DOM waits for the script engine to clean up the objects that belong to it, but the script engine has already terminated.
So, the problem is that the DOM only takes care of the memory that belongs to it and assumes the script engine will do the same.
I hope this helped.
See: http://msdn.microsoft.com/en-us/library/bb250448%28VS.85%29.aspx

Categories

Resources