4

I'm attempting to use Chrome Dev Tools to profile memory usage on my application and detect leaked DOM nodes and am puzzled by some behavior I'm seeing. According to the Chrome profiling docs, if I create DOM nodes and then properly release them, the tool should respond with a "Nodes" counter that resets to its baseline:

But if the sequence of actions is not expected to result in any retained memory, and the DOM node count does not drop down back to the baseline where you began, you have good reason to suspect there is a leak.

Methodology: I used an variation of the "Leaking DOM Nodes" example., modified to create large strings inside the nodes.

<html>
<head>
  <script>
  var leakedNodes = [];

  var largeStr = new Array(1000000).join('x');

  function createNode(text) {
    var div = document.createElement("div"),
      innerDiv = document.createElement("div"),
      textNode = document.createTextNode(text + " - " + new Date().toTimeString() + "-" + largeStr);
    innerDiv.appendChild(textNode);
    div.appendChild(innerDiv);
    return div;
  }

  function createLeakedNodes() {
    var i;
    for (i = 0; i < 200; i++) {
      leakedNodes.push(createNode("Leaked:" + i));
    }
  }

  function createGCNodes() {
    var i;
    for (i = 0; i < 200; i++) {
      createNode("Collected:" + i);
    }
  }

  function createNodes() {
    createLeakedNodes();
    createGCNodes();
  }

  function clearLeaks() {
    leakedNodes = null;
  }
  </script>
</head>

<body>
  <button onclick="createNodes()">Create</button>
  <button onclick="clearLeaks()">Clear Leaks</button>
</body>

</html>

I launch the page in an incognito tab with no extensions enabled, launch the dev tools, and start the timeline. I push the "Create" button three times, then the "Clear Leaks" button once, then click the "Collect Garbage" button in the profiler to force a GC, wait for the GC to finish, then stop the timeline.

I look at the "Nodes" counter in the profiler, and expected to see it climb for each of the times I pressed the "Create" button, and then drop, either after I click the "Clear Leaks" button or after I forced GC. However, what I see instead is that the graph climbs, but never drops.

Screenshot of Nodes counter

Is my understanding of the graph's behavior incorrect, or are the nodes legitimately not being released? If the latter, what code above is causing the leak, and why is the associated memory apparently being GCd as expected?

Palpatim
  • 9,074
  • 35
  • 43

2 Answers2

3

It seems that references to the DOM nodes are not destroyed if you dispose of reference to the array. But if you set array's length to zero, all elements in the array are disposed of (so all of the references to DOM elements) and then garbage collector can do it's work and free up the memory.

function clearLeaks() {
    leakedNodes.length = 0;
  }

I got this result by doing some research and using your example code :).

Harazi
  • 122
  • 1
  • 10
  • I can't confirm this behavior in my tests. Can you expand your answer with links to your research that suggested that setting the length to 0 would accomplish something that `null`ing the reference would not? – Palpatim Jun 18 '14 at 16:23
  • Also I have reduced the length of your string, now it is `var largeStr = new Array(100000).join('x');`, here is fiddle with my test code: http://jsfiddle.net/E7PNL/ And also http://stackoverflow.com/questions/4804235/difference-between-array-length-0-and-array . Here accepted answer tells about the difference of between disposing of array reference and destroying array elements. It might be that it didn't worked in your tests because of those strings, they are just too large :), on my pc even chrome crashed several times because of it... – Harazi Jun 19 '14 at 08:10
  • By the way, with `var largeStr = new Array(1000000).join('x');` even `createGCNodes()` ended up as leaks for me and were never disposed of... – Harazi Jun 19 '14 at 08:17
  • Thanks for the links and the discussion. I'd still like to understand the behavior of Chrome Dev Tools in this context, as my node counter doesn't reduce even setting `leakedNodes.length = 0`, but it's possible that something in the V8 internals is behaving differently from what I expect. The heap allocations profile definitely shows the leaked nodes being released. – Palpatim Jun 19 '14 at 14:44
  • Maybe I will be telling an obvious thing that you already know, but just in case: http://www.imagesup.net/?di=314031919383 . Also I found it harder to determine what is happening with really memory intense examples, because it takes time for chrome to react and also while Timeline is being observed and recorded it's even worse.. And I guess needless to say that you should only analyse recorded data after you have stopped the recording ;), although you are able to do that even when it is being recorded. – Harazi Jun 19 '14 at 15:51
  • Yep, I did know to let the recording run unmolested. :) My concern is still that the green "Nodes" line doesn't drop even after clearing the leaked nodes. I read that the "Nodes" line represent Chrome internal allocations, and doesn't directly impact the JS allocations metrics, so for now, that's my tentative explanation. – Palpatim Jun 19 '14 at 16:17
  • Well I don't know how it is in your test case, but it looks like the green "Nodes" line is directly related to selected range ;). However I found it to be not very accurate if compared to memory usage line, when memory consumption is very high :), however it is readable and if you select the right range you should see green "Nodes" line returning to normal :). – Harazi Jun 20 '14 at 07:20
1

I think there is a bug in chrome dev tools related to DOM node count as mentioned here. Profiling your code in canary does reduce the DOM count.

sushant
  • 49
  • 5