niloy / blog

blog raw files
3 stars 1 forks source link

Memory leaking pattern in JS #8

Open niloy opened 11 years ago

niloy commented 11 years ago

Memory leaking pattern in JS

<!DOCTYPE html>
<html>
<head>
  <title>Memory leaking</title>
  <script src="main.js"></script>
</head>
<body>
  <button id="add">Add Node</button>
  <button id="remove">Remove Node</button>
  <div id="nodes"></div>
</body>
</html>

The following js code will not leak memory.

window.onload = function() {
  var i = 0;

  add.addEventListener("click", function() {
    var d = document.createElement("div");
    d.textContent = "Node";
    d.addEventListener("click", function() {
      alert(i++);
    });
    nodes.appendChild(d);
  });

  remove.addEventListener("click", function() {
    nodes.removeChild(nodes.firstChild);
  });
};

Even though the click handler on the div is refererring the variable i in the closure, this is not going to create memory leaks. This is a feature of the mark and sweep garbage collection technique.

On the other hand, the following js will create memory leaks:

window.onload = function() {
  var i = 0;
  var arr = [];

  add.addEventListener("click", function() {
    var d = document.createElement("div");
    d.textContent = "Node";
    d.addEventListener("click", function() {
      alert(i++);
    });
    nodes.appendChild(d);
    arr.push(d);
  });

  remove.addEventListener("click", function() {
    nodes.removeChild(nodes.firstChild);
  });
};

We are keeping reference of the created div in the variable arr. So even if they get removed from the DOM, the reference in the arr will prevent it from garbage collected. One way to fix the problem is to remove the reference from the array when we remove the element from DOM as well. The remove click handler should be modified like this:

remove.addEventListener("click", function() {
  var index = arr.indexOf(nodes.firstChild);
  delete arr[index];
  nodes.removeChild(nodes.firstChild);
});