javascriptgarbage-collectiondocumentfragment

Garbage collections and DocumentFragment


I have read an article about memory leaks where the garbage collector logic is summarised to:

  1. The garbage collector builds a list of "roots". Roots usually are global variables to which a reference is kept in code. In JavaScript, the "window" object is an example of a global variable that can act as a root. The window object is always present, so the garbage collector can consider it and all of its children to be always present (i.e. not garbage).
  2. All roots are inspected and marked as active (i.e. not garbage). All children are inspected recursively as well. Everything that can be reached from a root is not considered garbage.
  3. All pieces of memory not marked as active can now be considered garbage. The collector can now free that memory and return it to the OS.

Additionally MDN states that DocumentFragment isn't part of an active DOM tree.

The DocumentFragment interface represents a minimal document object that has no parent. It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn't part of the active document tree structure, changes made to the fragment don't affect the document, cause reflow, or incur any performance impact that can occur when changes are made.

Bit by bit I start to realise the logic behind, but would appreciate a lot if someone can shine some light on me :), using the example below, and explain why:

1. It's considered good practice to nullify DOM references after they are being used.
2. Is there a need to nullify references to DocumentFragment and element that contains it.

function usefulFunction() {
  let existingNode = document.querySelector(`.existing`)
  
  let createdNode = document.createElement(`ul`)
  let fragment = document.createDocumentFragment();
  let browsers = ['Firefox', 'Chrome', 'Opera', 
      'Safari', 'Internet Explorer'];

  browsers.forEach(function(browser) {
      var li = document.createElement('li');
      li.textContent = browser;
      fragment.appendChild(li);
  });
    
  existingNode.appendChild(createdNode)
  createdNode.appendChild(fragment)
  
  fragment = null
  createdNode = null
  existingNode = null
}

usefulFunction()
<div class="existing"></div>

Updated snippet

let existingNode

function helperFunction(object) {
  let createdNode = document.createElement(`div`)
  createdNode.innerHTML = `Hello, I am a beautiful div`
  
  existingNode.appendChild(createdNode)
  existingNode = null 
}

function usefulFunction() {
  existingNode = document.querySelector(`.existing`)
  let fragment = document.createDocumentFragment();
  let browsers = ['Firefox', 'Chrome', 'Opera', 
      'Safari', 'Internet Explorer'];

  browsers.forEach(function(browser) {
      var li = document.createElement('li');
      li.textContent = browser;
      fragment.appendChild(li);
  });
      
  existingNode.appendChild(fragment)
  helperFunction()
}


usefulFunction()
<div class="existing"></div>


Solution

  • If you make proper use of local variables, there's generally little need to nullify them after you use them. When you leave the scope of the function, the variables go away, and any objects they refer to that aren't references from some variable that's still in scope will become garbage. Document fragments aren't referenced from the DOM, only from variables, so when the variables are destroyed, the fragments can be garbage collected.

    This is one of the reasons you should use global variables sparingly. They should only be used for data that needs to persist over time, e.g. to hold the state of the application.

    Note that in your first example code, nullifying the variables has no effect on garbage collection, because all the nodes and fragments that they contained were appended to the DOM. But if they weren't, those objects would become garbage as soon as the function ends, so there's no need to nullify the variables before returning.

    In the second snippet, you should nullify existingNode if you ever remove that element from the DOM. Otherwise, the global variable will prevent the node from being garbage collected. But if the node is expected to be permanent through the lifetime of the application, there's no need to worry about the variable.