I am trying to get rid of detached DOM Elements and having a hard time finding the cause of the leak.
Can somebody help me understand what native link from DOM wrapper stored in the detached window property is. What does it mean native link?
https://developer.chrome.com/devtools/docs/heap-profiling-dom-leaks
By tracing paths to window objects, it can be observed, that the
detached DOM tree is referenced as the native link from the DOM
wrapper stored in the detached window property. To confirm this, do
the following...
Any help will be appreciated!
In the example you have linked, there is a variable called 'detached' that is being created as a global on the window object.
window.detached
They then go on to generate an entire DOM tree with lots of children and extra data and they store that reference in the window.detached variable. It is not however actually mounted into the DOM.
The block that you have quoted is just pointing out that if you have any dom nodes you've generated that still have an active reference pointing to them (in this case the reference is window.detached) then they will not be garbage collected.
They go to the trouble of pointing this out because some people may expect that as soon as you unmount a tree of nodes from the DOM that they are candidates for GC. They're pointing out that what really matters is if there is still a reachable reference to the item. If not, it will be GC'ed. Otherwise it will hang around.
Related
Since I've tried out webworkers for the first time I'm struggling to find a real use case for them. Communication with them isn't as easy as just passing objects or references, they don't have a window object so I can't use JQuery and the increased complexity of building an interface is not worth the load saved on the main thread. So all the options I'm left with are basically working through large arrays to gain a performance advantage.
Today I thought about opening a new window by window.open() and use the newly created window object to do some task and pass the result back to the main window. I should even be able to access the DOM of the main window by accessing the window.openervariable in the new window.
My questions are:
Is that really going to give me a performance advantage?
Are there any caveats about this idea besides more complicated debugging?
Can I access the DOM of the main window from the new window using the window.opener variable and take the load of creating new DOM elements from the main thread?
Is that really going to give me a performance advantage?
No, as long as you can access window.opener or access objects from one tab to another, that means they share same instance of javascript interpreter, and javascript interpreter is single-threaded.
There is literally no (practical) way around this. You either have separate thread, or share same objects.
Are there any caveats about this idea besides more complicated debugging?
Main caveat: it does not work. Also caveat: separate window is probably not something suitable for production.
Can I access the DOM of the main window from the new window using the window.opener variable and take the load of creating new DOM elements from the main thread?
You can, but you should probably use correct document instance for calling document.createElement.
So all the options I'm left with are basically working through large arrays to gain a performance advantage.
That's exactly what workers are for, unless you are processing large amount of raw data, they are probably not a solution to your problem.
If you have performance drops when creating DOM nodes, you're most likely doing something wrong. Remember that:
createDocumentFragment exists for creating self contained element groups before appending them.
innerHTML causes DOM to rebalance tree in which you cahnged the HTML
new Text or document.createTextNode can be used to fill in text without innerHTML
If your page is scrollable table of many items, only those on screen need to be rendered.
You should also profile your code using developper tools to see where is the performance bottleneck. WebWorker is for data processing (eg. resizing images before upload), not for DOM manipulation.
Final note: it's the end of 2019, "I can't use jQuery" shouldn't be a problem any more. We now have document.querySelector and CSS animations, which were main uses of jQuery in the past.
I would like to know if detaching a div on load and appending that same div on a click will increase memory? When you detach a div (filled with images) is the div and image information kept in memory?
For example:
<div class="divContain-one">
<img src="test.png">
</div>
<div class="divContain-two"></div>
var divContainingImage = $("#divContain-one").detach();
$("button").click(function(){
$("#divContain-two").append(divContainingImage);
});
It shouldn't increase memory usage significantly. The variable simply contains a reference to same memory that held the DOM element, and any related jQuery data, before it was detached. All that has happened is that the DOM itself no longer has a reference to that memory.
When you append it, you don't increase the memory, either. It simply adds a reference to the same object to the new location in the DOM, it doesn't make a copy.
The information has to be kept in memory -- how else could it know what to add to the DOM when you append?
One caveat: the act of calling $("#divContainingImage") creates a jQuery object that wraps around the DOM element, and the variable contains a reference to this object, not just the DOM element. This does increase memory usage, independent of what .detach() does.
According to jquery documentaion detach method keeps all data associated with the removed element.
So, yes, especially taking the fact that you declared a variable and assigned returned data from detach method to it - data is definitely stored in memory. Anyway, keep in mind, that such memory usage is not significant if we are not talking about hundreds or thousands of elements.
You also need to consider if there are event listeners attached to the DOM object.
If you remove an object from the page then the memory can eventually be cleaned once there are no more references to it.
If you remove a and the has a click listener then that can continue to sit in memory indefinitely. Remove the listeners first then remove the DOM object. That way the DOM object can be removed from memory eventually.
In order to maintain a correct and highly responsive GUI overlay on a each website, I need to register and analyze every relevant DOM Element as soon as possible. Currently I am using a MutationObserver, which does this work for me and simplified, it looks like this:
var observer = new MutationObserver(
function(mutations){
mutations.forEach(
function(mutation){
if(mutation.type == 'childList')
{
var nodes = mutation.addedNodes;
var n = nodes.length;
for(var i = 0; i < n; i++)
{
if(nodes[i].nodeType == 1) // ELEMENT_NODE
{
// Save it in specific Array or something like this
AnalyzeNode(nodes[i]);
}
}
}
}
);
}
);
var config = {subtree: true, childList: true};
observer.observe(document, config);
But I've come to the realization, that the used MutationObserver isn't calling AnalyzeNode for every node contained in the DOM. When an already complete (sub)tree is created outside of the DOM (e.g. by executing an external JS script on page load) and you append its root to the DOM mutation.addedNodes will only contain the subtree's root and all of its children will go unnoticed (because no further mutations will take place there), being part of the DOM but not having been analyzed.
I had the idea of checking if the appended node may already have childNodes to identify it as root of an appended subtree, but unfortunately it seems like every addedNode may have children at the moment the MutationObserver's functions are called. So no distinction possible on this way.
I really don't want to double check every child node of an added node (the parent node) at the moment its parent node is processed by the MutationObserver. Most of the time, the child node will nevertheless be processed by the MutationObserver when itself will be part of addedNodes in an other occurring mutation and the overhead seems to get unnecessary high.
Furthermore, I thought about a Set of nodes, whose children have to be analyzed outside of a MutationObserver call. If an added node has children upon its appending to the DOM, the node is added to the Set. When another mutation takes place and one of its children is part of addedNodes, its child removes its parent from the Set by using mutation.target -- which is the parent node (mutation.type has to be childList). The problem with this approach is the timing when to check the children of the nodes in Set (and the fact, that I could query document.getElementsByTagname for every relevant Element type instead of maintaining a Set, but the timing problem is still there). Keep in mind that it should be as soon as possible to keep the overlay responsive and fitting to the website. A combination of document's onreadystatechange and appending of new script nodes to the DOM (as indicator when external JS code is executed) might work even for websites, recreating parts of its content (I am looking at you duckduckgo search result page). But it seems like a workaround, which won't solve the problem in 100% of the cases.
So, is there another, more efficient way? Or does any of these approaches may be sufficient if slightly changed? Thanks a lot!
(Please try to avoid JQuery where possible as example code, thank you. And by the way, I am using CEF, so the best case would be a solution working with Webkit/Blink)
EDIT1: Website rendering is done internally by CEF and GUI rendering is done by C++/OpenGL with information obtained by the mentioned Javascript code.
It seems your actual goal is to layout detect changes in the rendered output, not (potentially invisible) DOM changes.
On gecko based browsers you could use MozAfterPaint to get notified of the bounding boxes of changed areas, which is fairly precise but has a few gaps, such as video playback (which changes displayed content but not the layout) or asynchronous scrolling.
Layout can also be changed via the CSSOM, e.g. by manipulating a <style>.sheet.cssRules. CSS animations, already mentioned in the comments, are another thing that can also affect layout without mutations. And possibly SMIL animations.
So using mutation observers alone may be insufficient anyway.
If your overlay has some exploitable geometric properties then another possibility might be sampling the parts of the viewport that are important to you via document.elementFromPoint and calculating bounding boxes of the found elements and their children until you have whatever you need. Scheduling it via requestAnimationFrame() means you should be able to sample the state of the current layout on every frame unless it's changed by other rAF callbacks, running after yours.
In the end most available methods seem to have some gaps or need to be carefully tweaked to not hog too much CPU time.
Or does any of these approaches may be sufficient if slightly changed?
Combining tree-walking of observed mutations and a WeakSet to not process already visited nodes may work with some more careful filtering.
having already visited a node does not automatically mean you can skip its children
but having visited a child without it being a mutation target itself should mean you can skip it
removals events mean you must remove the entire subtree, node by node, from the set or just clear the set since they might be moved to another point in the tree
MutationRecords seem to be listed in the order in which the changes happened (can be easily verified).
Before you run your AnalyzeNode(nodes[i]) algorithm, you can run an AnalyzeChanges(mutations) step that can determine the over all change that happened.
For example, if you see addedNodes contains the same node 10 times, but you see the same node only 9 times in the removedNodes then you know that the net result is that the node was ultimately added to the DOM.
Of course it may be more complicated than that, you will have to detect added sub trees, and nodes that may have then been removed and added from those sub trees, etc.
Then finally, once you know what the net change was, you can run AnalyzeNode(nodes[i]).
I'm thinking about doing this to observe an entire <svg> tree and to render it (and re-render it when changes happen) in WebGL.
It may be tricky, because imagine the following happens synchronously by some user (you don't know what he/she will do) who is manipulating the DOM:
a subtree is added (queues a record for the root node in addedNodes)
a subtree of the subtree is removed (queues a record)
then appended somewhere else outside of the first subtree (queues another record, oh boy.)
an element is removed from the other subtree is removed (queues a record)
and added back to the original subtree (queues a record)
etc
etc
Finally, you receive a list of MutationRecords that details all those steps.
We could loop through all the records and basically recreate a play-by-play of what happened, then we can figure out the final net changes that happened.
After we have those net changes, it'll be like having a list of records, but they will be simpler, (for example removing then adding a node simply cancels it out, so we don't really care if a node was removed then immediately added because the end result is basically nothing for that node).
People have tried to tackle the problem of detecting changes between trees.
Many of those solutions are associated with the terms "virtual dom" and "dom diffing" as far as web goes, which yield results on Google.
So, instead of doing all that mutation analysis (which sounds like a nightmare, though if you did it I would say please please please publish it as open source so people can benefit), we can possibly use a diffing tool to find the difference between the DOM before the mutation, and the DOM at at the time the MutationObserver callback is fired.
For example, virtual-dom has a way to get a diff. I haven't tried it yet. I'm also not sure how to create VTrees from DOM trees to pass them into the diff() function.
Aha! It may be easier to get a diff with diffDOM.
Getting a diff might present the simplest changeset needed to transition from one tree to another tree, which might be much easier than analyzing the a mutation record list. It's worth trying it out. I might post back what I find when I do it with the <svg>s...
MDN says this is one way to remove all children from a node. But since only the first child node is referenced in code, do the others become memory orphans? Is anything known about whether this is the case in any or all browsers? Is there something in the DOM standard that calls for garbage collection when doing this?
I guess you are referring to this example
// This is one way to remove all children from a node
// box is an object reference to an element with children
while (box.firstChild) {
//The list is LIVE so it will re-index each call
box.removeChild(box.firstChild);
}
No it does not cause a memory leak.
What happens is after the 1st child is removed the 2nd one will take it's place as the 1st child, and so on until there are no more children left.
Also garbage collection can not be usually requested on demand, the virtual machine will do it when it thinks it can, and that does differ between browsers.
Im using firebug to debug jquery and in the dom tab i can see all the vars and arrays in there.
How do I access the different arrays and vars in the dom?
Cheers
Ke
I cannot access these object items, even though firefox lists them, i have sitems in the top level of the dom, i also have sitems within the parent variable.
a lot of head scratching happening here, would be grateful for any help :)
Looks like you want to access a user defined property, since these are not properties of the DOM ( Firebug Wiki DOM panel page. ), I don't think you can access them directly through your page, but you can access them through the Firebug console.
Simply type the name of the property into the command line of the Console... the part after >>> on the very bottom.
In your case you would type something like: sitems[0] and hit enter.
To access properties of the DOM... take a look at the DOM exploration page for Firebug.
To see how to access properties, functions, or constants of the DOM, check what you're interested in in the DOM tab.
Then you can "follow the bread crumbs" to access properties directly. Global properties are attached to window, so you don't need to include window:
Make sure to right click on things and explore the context menu, especially if you start looking at functions.
If it's an array you should access it as an array by referencing the index in the Array you are trying to access.
alert(sitems[1]);
If it's an object you can reference by using the "key" for the property or method of the object you are trying to access:
alert(sitems["keyName"]);
Likewise some of the stuff you'll see in the DOM tab are actually references to methods and objects within the DOM, so if you're going to call them or reference them you need to do so based on their type, or you may even need to provide arguments to them in order to get a response.
It's giving 'undefined' because you can't output the contents of an Array just by calling its name.