3

So I noticed my application getting slow after some extended usage. Particularly when I create many views that are each items in a long list. I had thought that by removing these views with view.remove() would help remedy the problem but while they are removed from the page, I noticed that Chrome's Timeline shows that my DOM Node Count does not reduce. Each view I add continues to grow this count. As a matter of fact, the only thing that seems to reduce this Node Count is a page refresh.

This doesn't feel right to me and I feel like I messed up something really elementary since the problem seems to occur with all my views and not just these ones. It just happens much quicker with these view lists since there are so many of them.

Does anyone have any suggestions as to what I should be looking for? What kind of causes can produce this behavior?

I'd provide code but I don't know what would be helpful.

TL;DR - View.remove() is removing the view from the page, but my DOM Node Count continues to go up and never comes down.

jmk2142
  • 8,581
  • 3
  • 31
  • 47
  • Did you check if this is reproduce able in other browsers?may be a bug on chrome's web inspector. It is perfectly fine for having less than 1200 Dom nodes when it goes more than that you will have to check which views are not displayed and remove them immediately – Deeptechtons Jul 21 '12 at 05:24
  • 1
    Are your views listening to any model or collection events? That could cause leaks through `this.el` and `this.$el` in the views. – mu is too short Jul 21 '12 at 05:31
  • @Deeptechtons I wish I were good enough to say it's a Chrome inspector problem, but I'm PRETTY darn sure the weakest link is sitting in front of this computer. ;-) To note, I thought I should clarify that my DOM Elements are being removed. Something like document.getElementsByTagName("*").length results in appropriate count changes depending on whether I add or remove views. I use Derick Baileys zombie removal ideas and each view has a close function, that both removes and unbinds events successfully. So my events drop, views are gone (on surface at least). But the Node Count remains. – jmk2142 Jul 21 '12 at 05:44
  • @muistooshort Yes. My various views do listen to models and collections. I also have an extended close() method which calls both remove() and onClose(). onClose() basically has all the unbind('event') operations to undo any binds I've made to the various models and collections. But could you elaborate on what you mean about leaks THROUGH this.el/$el? – jmk2142 Jul 21 '12 at 05:48
  • 3
    If a view is still listening to a model then the model has a reference to the view, the view will have a reference to the DOM element in its `el` and `$el` properties. This means that the model has an indirect reference to the element and you have zombies. – mu is too short Jul 21 '12 at 06:12
  • I'll get back to you on this... gotta check something out. :-P – jmk2142 Jul 21 '12 at 06:14
  • Okay. So it looks like I forgot to add an unbind(). I'm starting to see the DOM Node Count drop, which is good. However, I notice that the drop doesn't happen as soon as the View is removed. Sometimes it takes a full 2 min. Sometimes even longer (4-6min) before the DOM Node drops back down completely. What causes this behavior? Meanwhile performance with certain animation effects drops if the node count is high. Basically I have these somewhat element rich list items generated through an infinite scroll and if someone is going through the list fast, it produces nodes very quickly. – jmk2142 Jul 21 '12 at 08:06
  • @muistooshort I think I got it. Based on your suggestions and some more homework I've learned about the pitfalls of circular referencing, how DOM nodes and listeners play together, garbage collection, and what to expect. After some refactoring, my program performs much better. Even on "challenge setting" (trying to load as many nodes as quickly as possible) my code cleans itself up and never gets too high, destroying what it doesn't need. Looks better than I expected. Animations are silky smooth. I owe you a pie. Thanks! – jmk2142 Jul 22 '12 at 01:26
  • If you haven't already upgraded to the newest version of Backbone, you should definitely do that. I'm working on an application that suffered from some leaking issues which using `listenTo` and `stopListening` (both new backbone methods) solved all of our problems. Check out my SO post http://stackoverflow.com/questions/15126334/backbone-js-memory-management-rising-dom-node-count – Cory Danielson Mar 15 '13 at 06:06

1 Answers1

6

You have a memory leak caused by not cleaning up your views properly.

Read this: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/

and this: http://lostechies.com/derickbailey/2012/03/19/backbone-js-and-javascript-garbage-collection/

You need to do more than just call .remove() on your views. You need to properly destroy all of the events and other bindings that are hanging around when you try to close your views. A common way to do this is to provide a close method on views, which I describe in the first post.

Be sure to read the comment from Johnny Oshika on that first post, too. It points to a great way to implement event cleanup.

Derick Bailey
  • 72,004
  • 22
  • 206
  • 219
  • Thanks for the answer, but I think mu solved my problem in the comments. Actually, I even mentioned you and your zombie technique in the discussion. :-) What is the etiquette for resolving this question in this case? I had not read the garbage collection article you linked so that was useful, just like all your posts. – jmk2142 Jul 22 '12 at 22:05