33

I have noticed that the memory of the browser starts increasing whilst I am in a form (this is noticeable from the task manager). In IE 9, this goes easily over 500MB after some usage, whilst chrome is more resilient (goes to 200MB using same test).

I am using the chrome developer tools to debug this issue. I have noticed that there is a large number of Detached DOM tree:

detached dom tree screenshot

I am assuming that this can confirm that there is a memory leak. Would that be correct? Secondly, I would need to find out how to identify the root cause of the problem. I know that you should use the retaining tree to identify what is stopping those items from being reclaimed. But I cannot find out how to use the retaining tree. For example, what does the retaining tree in the above screenshot mean please?

Any assistance would be greatly appreciated.

Joseph Caruana
  • 2,241
  • 3
  • 31
  • 48
  • 2
    Could you clarify `memory of the browser starts increasing whilst I am in a form` with some code? That helps to find memory leak. – Ikrom Jun 03 '13 at 17:32
  • Does [this other question/answer](http://stackoverflow.com/questions/11930050/finding-js-memory-leak-in-chrome-dev-tools) help you? – msung Jun 04 '13 at 07:22

2 Answers2

27

There are many considerations to keep in mind when you write code that references DOM elements. But it all basically boils down to a couple of simple points -

a. Within your local functions always clear the reference

var menu = $('body #menu');
// do something with menu
 .
 .
 .
 menu = null;

b. Never store references as part of element data .data()

c. Try not to use DOM references inside closures/inline handlers, instead pass identifiers

    function attachClick(){
      var someDiv = $('#someDiv');

      someDiv.click(function(){
         var a = someDiv....;
         //Wrong. Instead of doing this..
      });


      someDiv.click(function(){
         var a = $('#someDiv');
         //Pass the identifier/selector and then use it to find the element
      });       


      var myFunc = function(){
         var a = someDiv;
         //using a variable from outside scope here - big DON'T!             
      }
    }

Yes, one can argue that searching elements can slow the page down, but the delay is very minimal when compared to the performance hit a huge heap causes esp. in large single page applications. Hence, #3 should be used only after weighing the pros and cons. (It did help significantly in my case)

UPDATE

d. Avoid anonymous functions - Naming your event handlers and local functions will help you a lot while profiling/looking at heap snapshots.

Robin Maben
  • 22,194
  • 16
  • 64
  • 99
  • I'd have to disagree with (c), the only time this would cause an issue imo would be if the subsequent function call returned a closure, or performed an operation that retained a reference to the passed in dom element (or jQuery wrapped element) -- i.e. like storing the element in a static list, or creating some kind of circular reference. Do you have an example of where simply passing an element to a method would cause an issue? – Pebbl Jun 05 '13 at 11:19
  • 2
    @pebbl:I checked with my code. You're right. I meant closures and not simply passing them to methods. Corrected example above. – Robin Maben Jun 06 '13 at 09:54
  • Your very last example - not using variables from outside scope - why not? Isn't that the whole point of using a closure? – Alex McMillan Dec 19 '13 at 19:44
  • 1
    @AlexMcMillan: You can go ahead and use it if you don't care that you're passing references to DOM objects! It's not much of a problem in the case of primitive values/objects. – Robin Maben Dec 20 '13 at 01:34
  • @RobinMaben, suppose you need to search for the identifier within a specific element, such as `$('#someDiv', targetEl)`. Suppose `targetEl` is an argument of `myFunc` which references a DOM element the identifier for which is not known within the `myFunc` body. How would you recommend maintaining the DOM reference? – Vinney Kelly Sep 30 '14 at 16:27
  • 2
    I don't agree with these tips. (a) is unnecessary; whenever the scope is left (e.g. when you're leaving a local function), as long as there are no remaining references to the reference, it will be cleaned up. (b) is fine to do if you're responsible with how many references you're storing; it doesn't matter where you store them. (c) same thing. Just be responsible with how many references you store. Using variables from a higher scope is fine and can have performance improvements. The bigger problem is that you're likely creating elements and detaching them instead of actually deleting them. – ZachB Feb 01 '15 at 02:51
  • 2
    Mind also any Event listeners that are attached to DOM element and not removed when the DOM element is detached – fadomire Mar 23 '15 at 10:02
  • 1
    @RobinMaben quick question in example c, your example makes sense but wouldn't that suggest that ideally you are not referencing the variable `el` to add the click handler? – aug Feb 13 '17 at 22:31
2

Looks like your code creates many DOM subtrees and keeps references to it from javascript. You need to select an element from a Detached dom tree. According to the snapshot you should select Text element. And look into the retainers tree.

This tree shows you all the paths that keeps the object alive. At least one path, usually the shortest one, will lead you to the window object. If you familiar with the code then you may easily find the object in that path that has to be deleted but it doesn't. There can be many such objects in the path. The object with the smallest distance is more interesting.

loislo
  • 14,025
  • 1
  • 28
  • 24