Loading ...
Sorry, an error occurred while loading the content.

Yahoo best coding practices to avoid memory leaks

Expand Messages
  • bubbarubadub
    Hello All, I am developing a web application for my company that uses the Yahoo UI DD (Drag and Drop) class to place all content in movable modules. I am also
    Message 1 of 5 , Aug 22, 2006
    • 0 Attachment

      Hello All,

      I am developing a web application for my company that uses the Yahoo UI DD (Drag and Drop) class to place all content in movable modules.  I am also using several Yahoo DOM classes.

      My server requests all return raw html or JSON.  I messed with Google's AJAXSLT and Sarissa and the documentation is subpar or the code is buggy.

      The problem is my application is having a good bit of memory leaks.  I've read several articles (such as this one on MSDN) online about the causes, but I'm still a bit mystified. 

      It seems one memory leak problem deals with replacing DOM nodes that have event handlers already attached.  Another problem deals with functions declared within functions.  It looks as if most of Yahoo's custom classes have this property.

      Does Yahoo have any suggestions on how to deal with these issues, since I'm assuming they must be handled by any AJAX type web application? 

      Thanks for any help in advance, and I've been very pleased with the library!

      Randall T.

    • Isaac Schlueter
      Randall, This is a great question, and a topic that has a lot of mysticism surrounding it. Like most Javascript issues, there s been a lot of very bad
      Message 2 of 5 , Aug 22, 2006
      • 0 Attachment
        Randall,

        This is a great question, and a topic that has a lot of mysticism surrounding it.  Like most Javascript issues, there's been a lot of very bad "authoritative" suggestions.

        First of all, if you don't use a polling mechanism of some kind, AJAX applications will leak memory like a bucket with no bottom.  Use the YUI connection library for Ajax, and never look back.  It's brilliant, and very easy to use.

        A good discussion on memory leaks and why and how they happen: http://www.crockford.com/javascript/memory/leak.html
        The claim that closures cause memory leaks is, as Crockford says, "deeply wrong."  Closures are fine, and have nothing to do with the problem.

        The problem happens when you have a Javascript object and DOM object that refer to one another in a cycle.  IE can't figure out when it should reclaim the memory, so it doesn't ever do it.

        For example, this will cause a leak:
        <script>
        (function(){
        var obj={b:document.body};
        document.body.o=obj;
        })();
        </script>
        If you set either obj.doc.body or body.o to NULL, then you'll break the circular chain, and IE will reclaim the memory.

        The cycle doesn't have to be so small.  Even a chain of many steps can cause a leak if it is not broken.  This will cause a leak, too:
        <script>
        (function(){
        var d={b:document.body}
        var obj={doc:d}; // obj.doc.body === document.body
        document.body.o=obj;
        })();
        </script>

        The simplest way to ensure that you will never have a memory leak is to simply never have circular reference chains that cross between Javascript and DOM space.  Make sure that you always have Javascript objects refer to DOM objects, and never the other way round, or vice versa.

        However, it's sometimes extremely convenient to have circular link.  Consider this example:

        <script>
        (function(){
        var doSomething=function(e) {
            this.innerHTML='did something!';
            this.object.doSomethingElse(this.customPropertyOfSomeKind);
        };
        myDomNode.object=new myObject();
        myDomNode.customPropertyOfSomeKind={some:'data object'};
        YAHOO.util.Event.addListener(myDomNode,'click',doSomething);
        })();
        </script>

        Now, if myObject has any reference to myDomNode (even if it refers to something that refers to something else ... that refers to myDomNode), you'll leak memory.

        So, how to fix this?

        First, be aware when you're doing things that may cause a leak.  If it's not a very big gain in code simplicity, then figure out another way around.  If you're hanging a lot of Javascript objects onto DOM objects, there's a big chance of a leak creeping in.  Personally, I try to make sure that all my references go from JS-->DOM and not the other way around.  If the references are always one-way, then there's no chance of a leak.  Also, we've seen performance issues with hanging too much stuff on DOM nodes if the page is big and complicated (that's anecdotal, and I don't have any hard numbers, so take it for what it's worth.)
        If you understand how they work and why they happen, you can save yourself a lot of time later on tracking them down.

        Second, test your code with Drip.
        http://outofhanwell.com/ieleak/index.php?title=Main_Page
        I can't possibly stress how important this is.  Even if you've done everything right, it's easy to overlook circular references if the code gets sufficiently complex.  Even small memory leaks can add up.

        Third, if you must cause circular references in your code, be responsible about it.  Save a reference to each afflicted DOM node, and break the cycles on window unload.
        <script>
        (function(){
        var unLoaders=[];
        myDomNode.object=new myObject(); // <--- let's say that this creates a leak somewhere
        unLoaders.push(myDomNode); // <--- save it for later
        var unload=function(){
            for(var i=unLoaders.length-1;i>-1;i--){
               unLoaders[i].object=null; // <--- break the cycle
            }
        };
        YAHOO.util.Event.addListener(window,'unload',unload);
        })();
        </script>

        So, why do AJAX apps leak memory so badly if you don't use the yui Connection library?  Consider the "typical" XHR code pattern:
        <script>
        (function(){
        var x=getXHRobject();
        x.onreadystatechange=function() {
            if(x.readystate==4){
               // do something.
            }
        };
        })();
        </script>
        The XmlHttpRequest object is treated in Javascript much like a DOM node.  If you attach an onreadystatechange handler to it, you've created a circular loop.  The standard means of breaking these chains won't work.  The YUI connection lib polls the object's readystate until it is done, and then calls your success function.  (If it times out or gets an error, it calls your failure function.)  Since there's no onreadystatechange listener, there's no circular reference, and thus, no memory leak.

        --
        Isaac Z. Schlueter
        Webdev, Yahoo! Games


        ----- Original Message ----
        From: bubbarubadub <randall.toepfer@...>
        To: ydn-javascript@yahoogroups.com
        Sent: Tuesday, August 22, 2006 10:20:54 AM
        Subject: [ydn-javascript] Yahoo best coding practices to avoid memory leaks

        Hello All,

        I am developing a web application for my company that uses the Yahoo UI DD (Drag and Drop) class to place all content in movable modules.  I am also using several Yahoo DOM classes.

        My server requests all return raw html or JSON.  I messed with Google's AJAXSLT and Sarissa and the documentation is subpar or the code is buggy.

        The problem is my application is having a good bit of memory leaks.  I've read several articles (such as this one on MSDN) online about the causes, but I'm still a bit mystified. 

        It seems one memory leak problem deals with replacing DOM nodes that have event handlers already attached.  Another problem deals with functions declared within functions.  It looks as if most of Yahoo's custom classes have this property.

        Does Yahoo have any suggestions on how to deal with these issues, since I'm assuming they must be handled by any AJAX type web application? 

        Thanks for any help in advance, and I've been very pleased with the library!

        Randall T.


      • Scott Schiller
        bubbarubadub , I m a fan of using destructors in objects and having related methods which can assign and remove event handlers and DOM node references, as one
        Message 3 of 5 , Aug 22, 2006
        • 0 Attachment
          "bubbarubadub",
           
          I'm a fan of using destructors in objects and having related methods which can assign and remove event handlers and DOM node references, as one approach to preventing memory leaks. (I work on the new Yahoo! Photos, and memory management is an area we've done an extensive amount of testing in due to the large numbers of objects we deal with.)
           
          A common leak "pattern" problem is:
           
          function SomeObject(img) {
            this.mouseOver = function() {
              // some event handler
            }
            this.o = img; // pointer to DOM node..
            this.o.onmouseover = this.mouseOver; // this object -> DOM -> this object = memory leak
          }
           
          var so = new SomeObject(document.getElementById('someImage'));
           
          You need to break the cycle by setting this.o.onmouseover to null, and then (perhaps because I'm paranoid and it doesn't hurt,) set this.o to null as well. This way, you guarantee the "freeing" of JS objects from holding references to the DOM.
           
          In the object-oriented case where numerous instances of these are being generated (as with Photos), it helps to take a flexible approach.
           
          E.g., an object might have a .destructor() method which calls a releaseEvents() method which removes attached event handlers, sets DOM node references such as "this.o" (pointing to an IMG or what have you) to null, and so on, in that order, effectively preventing the common circular reference leak.
           
          I think this method works well, provided that you have the discipline to remember to include and call these kind of methods - effectively, cleaning up after yourself. (We shouldn't have to, but this is how it is.)
           
          As a side note (and plug!), we have a planned series of articles for the YUI blog (http://yuiblog.com/) regarding some of our technical findings relating to performance issues, memory leaks and so on, while working on Photos.
           
          There are also lots of web resources you can look up on recommended techniques, but I find sometimes you have to dig into your own code after reading some suggested approaches, click around and cause some functions to run, reload your browser lots (repeating the process and so on,) and sometimes test for leaks through process of elimination. It can be frustrating, but also a relief when you find you can be developing and working, refreshing the same page all day, without having to eventually kill the browser process. Think of all the users whose RAM you'll be saving! ;)
           
          For testing a "specific kind of leak," there's a firefox memory leak extension which I've found to be handy. It appears to watch for circular references (JS object -> DOM -> JS object, ie., assigning an event handler,) but I'm not certain from the description from the Mozilla site below: "It warns when chrome windows close but leave other code pointing at their JavaScript objects."
           
          It might be a bit aggressive as it will report leaks whenever you leave any page that it detects them on, but I've found it handy for my purposes. (I'm glad to say I haven't seen it bring up any items for Photos, knock on wood.)
           
          Hope this was of some help regarding your question.
           
          Oh and P.S., before I forget: The whistles go, "woooooo!" ;)
           
           
          Kind regards,
           
          Scott Schiller
          Front-end Engineer, Yahoo! Photos

           
          ----- Original Message ----
          From: Isaac Schlueter <isaac_schlueter@...>
          To: ydn-javascript@yahoogroups.com
          Sent: Tuesday, August 22, 2006 11:04:32 AM
          Subject: Re: [ydn-javascript] Yahoo best coding practices to avoid memory leaks

          Randall,

          This is a great question, and a topic that has a lot of mysticism surrounding it.  Like most Javascript issues, there's been a lot of very bad "authoritative" suggestions.

          First of all, if you don't use a polling mechanism of some kind, AJAX applications will leak memory like a bucket with no bottom.  Use the YUI connection library for Ajax, and never look back.  It's brilliant, and very easy to use.

          A good discussion on memory leaks and why and how they happen: http://www.crockford.com/javascript/memory/leak.html
          The claim that closures cause memory leaks is, as Crockford says, "deeply wrong."  Closures are fine, and have nothing to do with the problem.

          The problem happens when you have a Javascript object and DOM object that refer to one another in a cycle.  IE can't figure out when it should reclaim the memory, so it doesn't ever do it.

          For example, this will cause a leak:
          <script>
          (function(){
          var obj={b:document.body};
          document.body.o=obj;
          })();
          </script>
          If you set either obj.doc.body or body.o to NULL, then you'll break the circular chain, and IE will reclaim the memory.

          The cycle doesn't have to be so small.  Even a chain of many steps can cause a leak if it is not broken.  This will cause a leak, too:
          <script>
          (function(){
          var d={b:document.body}
          var obj={doc:d}; // obj.doc.body === document.body
          document.body.o=obj;
          })();
          </script>

          The simplest way to ensure that you will never have a memory leak is to simply never have circular reference chains that cross between Javascript and DOM space.  Make sure that you always have Javascript objects refer to DOM objects, and never the other way round, or vice versa.

          However, it's sometimes extremely convenient to have circular link.  Consider this example:

          <script>
          (function(){
          var doSomething=function(e) {
              this.innerHTML='did something!';
              this.object.doSomethingElse(this.customPropertyOfSomeKind);
          };
          myDomNode.object=new myObject();
          myDomNode.customPropertyOfSomeKind={some:'data object'};
          YAHOO.util.Event.addListener(myDomNode,'click',doSomething);
          })();
          </script>

          Now, if myObject has any reference to myDomNode (even if it refers to something that refers to something else ... that refers to myDomNode), you'll leak memory.

          So, how to fix this?

          First, be aware when you're doing things that may cause a leak.  If it's not a very big gain in code simplicity, then figure out another way around.  If you're hanging a lot of Javascript objects onto DOM objects, there's a big chance of a leak creeping in.  Personally, I try to make sure that all my references go from JS-->DOM and not the other way around.  If the references are always one-way, then there's no chance of a leak.  Also, we've seen performance issues with hanging too much stuff on DOM nodes if the page is big and complicated (that's anecdotal, and I don't have any hard numbers, so take it for what it's worth.)
          If you understand how they work and why they happen, you can save yourself a lot of time later on tracking them down.

          Second, test your code with Drip.
          http://outofhanwell.com/ieleak/index.php?title=Main_Page
          I can't possibly stress how important this is.  Even if you've done everything right, it's easy to overlook circular references if the code gets sufficiently complex.  Even small memory leaks can add up.

          Third, if you must cause circular references in your code, be responsible about it.  Save a reference to each afflicted DOM node, and break the cycles on window unload.
          <script>
          (function(){
          var unLoaders=[];
          myDomNode.object=new myObject(); // <--- let's say that this creates a leak somewhere
          unLoaders.push(myDomNode); // <--- save it for later
          var unload=function(){
              for(var i=unLoaders.length-1;i>-1;i--){
                 unLoaders[i].object=null; // <--- break the cycle
              }
          };
          YAHOO.util.Event.addListener(window,'unload',unload);
          })();
          </script>

          So, why do AJAX apps leak memory so badly if you don't use the yui Connection library?  Consider the "typical" XHR code pattern:
          <script>
          (function(){
          var x=getXHRobject();
          x.onreadystatechange=function() {
              if(x.readystate==4){
                 // do something.
              }
          };
          })();
          </script>
          The XmlHttpRequest object is treated in Javascript much like a DOM node.  If you attach an onreadystatechange handler to it, you've created a circular loop.  The standard means of breaking these chains won't work.  The YUI connection lib polls the object's readystate until it is done, and then calls your success function.  (If it times out or gets an error, it calls your failure function.)  Since there's no onreadystatechange listener, there's no circular reference, and thus, no memory leak.

          --
          Isaac Z. Schlueter
          Webdev, Yahoo! Games


          ----- Original Message ----
          From: bubbarubadub <randall.toepfer@...>
          To: ydn-javascript@yahoogroups.com
          Sent: Tuesday, August 22, 2006 10:20:54 AM
          Subject: [ydn-javascript] Yahoo best coding practices to avoid memory leaks

          Hello All,

          I am developing a web application for my company that uses the Yahoo UI DD (Drag and Drop) class to place all content in movable modules.  I am also using several Yahoo DOM classes.

          My server requests all return raw html or JSON.  I messed with Google's AJAXSLT and Sarissa and the documentation is subpar or the code is buggy.

          The problem is my application is having a good bit of memory leaks.  I've read several articles (such as this one on MSDN) online about the causes, but I'm still a bit mystified. 

          It seems one memory leak problem deals with replacing DOM nodes that have event handlers already attached.  Another problem deals with functions declared within functions.  It looks as if most of Yahoo's custom classes have this property.

          Does Yahoo have any suggestions on how to deal with these issues, since I'm assuming they must be handled by any AJAX type web application? 

          Thanks for any help in advance, and I've been very pleased with the library!

          Randall T.




        • bigtreestechman
          Pls forgive my ignorance... Are these memory leaks isolated to IE? Has testing been done with IE7 (the real one, not the JS patch)? If so, what were the
          Message 4 of 5 , Aug 23, 2006
          • 0 Attachment
            Pls forgive my ignorance...
            Are these memory leaks isolated to IE?
            Has testing been done with IE7 (the real one, not the JS patch)? If
            so, what were the results?
            MS says that later this year they are going to push IE7 out as a high
            priority security update to Windows XP.

            Thanks,
            joe
          • bubbarubadub
            Thanks for the help! I look forward to the YUI blog update. I found Crockford s page helpful, and I also came across a coding guideline on another
            Message 5 of 5 , Sep 1, 2006
            • 0 Attachment
              Thanks for the help! I look forward to the YUI blog update. I found
              Crockford's page helpful, and I also came across a coding guideline
              on another developer's website that I have found very helpful:

              http://weblogs.java.net/blog/gmurray71/archive/2006/03/javascript_reco
              .html

              Thanks again,
              Randall


              --- In ydn-javascript@yahoogroups.com, Isaac Schlueter
              <isaac_schlueter@...> wrote:
              >
              > Randall,
              >
              > This is a great question, and a topic that has a lot of mysticism
              surrounding it. Like most Javascript issues, there's been a lot of
              very bad "authoritative" suggestions.
              >
              > First of all, if you don't use a polling mechanism of some kind,
              AJAX applications will leak memory like a bucket with no bottom. Use
              the YUI connection library for Ajax, and never look back. It's
              brilliant, and very easy to use.
              >
              > A good discussion on memory leaks and why and how they happen:
              http://www.crockford.com/javascript/memory/leak.html
              > The claim that closures cause memory leaks is, as Crockford
              says, "deeply wrong." Closures are fine, and have nothing to do with
              the problem.
              >
              > The problem happens when you have a Javascript object and DOM
              object that refer to one another in a cycle. IE can't figure out
              when it should reclaim the memory, so it doesn't ever do it.
              >
              > For example, this will cause a leak:
              > <script>
              > (function(){
              > var obj={b:document.body};
              > document.body.o=obj;
              > })();
              > </script>
              > If you set either obj.doc.body or body.o to NULL, then you'll
              break the circular chain, and IE will reclaim the memory.
              >
              > The cycle doesn't have to be so small. Even a chain of many steps
              can cause a leak if it is not broken. This will cause a leak, too:
              > <script>
              > (function(){
              > var d={b:document.body}
              > var obj={doc:d}; // obj.doc.body === document.body
              > document.body.o=obj;
              > })();
              > </script>
              >
              > The simplest way to ensure that you will never have a memory leak
              is to simply never have circular reference chains that cross between
              Javascript and DOM space. Make sure that you always have Javascript
              objects refer to DOM objects, and never the other way round, or vice
              versa.
              >
              > However, it's sometimes extremely convenient to have circular
              link. Consider this example:
              >
              > <script>
              > (function(){
              > var doSomething=function(e) {
              > this.innerHTML='did something!';
              > this.object.doSomethingElse(this.customPropertyOfSomeKind);
              > };
              > myDomNode.object=new myObject();
              > myDomNode.customPropertyOfSomeKind={some:'data object'};
              > YAHOO.util.Event.addListener(myDomNode,'click',doSomething);
              > })();
              > </script>
              >
              > Now, if myObject has any reference to myDomNode (even if it refers
              to something that refers to something else ... that refers to
              myDomNode), you'll leak memory.
              >
              > So, how to fix this?
              >
              > First, be aware when you're doing things that may cause a leak. If
              it's not a very big gain in code simplicity, then figure out another
              way around. If you're hanging a lot of Javascript objects onto DOM
              objects, there's a big chance of a leak creeping in. Personally, I
              try to make sure that all my references go from JS-->DOM and not the
              other way around. If the references are always one-way, then there's
              no chance of a leak. Also, we've seen performance issues with
              hanging too much stuff on DOM nodes if the page is big and
              complicated (that's anecdotal, and I don't have any hard numbers, so
              take it for what it's worth.)
              > If you understand how they work and why they happen, you can save
              yourself a lot of time later on tracking them down.
              >
              > Second, test your code with Drip.
              > http://outofhanwell.com/ieleak/index.php?title=Main_Page
              > I can't possibly stress how important this is. Even if you've done
              everything right, it's easy to overlook circular references if the
              code gets sufficiently complex. Even small memory leaks can add up.
              >
              > Third, if you must cause circular references in your code, be
              responsible about it. Save a reference to each afflicted DOM node,
              and break the cycles on window unload.
              > <script>
              > (function(){
              > var unLoaders=[];
              > myDomNode.object=new myObject(); // <--- let's say that this
              creates a leak somewhere
              > unLoaders.push(myDomNode); // <--- save it for later
              > var unload=function(){
              > for(var i=unLoaders.length-1;i>-1;i--){
              > unLoaders[i].object=null; // <--- break the cycle
              > }
              > };
              > YAHOO.util.Event.addListener(window,'unload',unload);
              > })();
              > </script>
              >
              > So, why do AJAX apps leak memory so badly if you don't use the yui
              Connection library? Consider the "typical" XHR code pattern:
              > <script>
              > (function(){
              > var x=getXHRobject();
              > x.onreadystatechange=function() {
              > if(x.readystate==4){
              > // do something.
              > }
              > };
              > })();
              > </script>
              > The XmlHttpRequest object is treated in Javascript much like a DOM
              node. If you attach an onreadystatechange handler to it, you've
              created a circular loop. The standard means of breaking these chains
              won't work. The YUI connection lib polls the object's readystate
              until it is done, and then calls your success function. (If it times
              out or gets an error, it calls your failure function.) Since there's
              no onreadystatechange listener, there's no circular reference, and
              thus, no memory leak.
              >
              > --
              > Isaac Z. Schlueter
              > Webdev, Yahoo! Games
              >
              >
              > ----- Original Message ----
              > From: bubbarubadub <randall.toepfer@...>
              > To: ydn-javascript@yahoogroups.com
              > Sent: Tuesday, August 22, 2006 10:20:54 AM
              > Subject: [ydn-javascript] Yahoo best coding practices to avoid
              memory leaks
              >
              >
              > Hello All,
              > I am developing a web application for my company that uses the
              Yahoo UI DD (Drag and Drop) class to place all content in movable
              modules. I am also using several Yahoo DOM classes.
              > My server requests all return raw html or JSON. I messed with
              Google's AJAXSLT and Sarissa and the documentation is subpar or the
              code is buggy.
              > The problem is my application is having a good bit of memory
              leaks. I've read several articles (such as this one on MSDN) online
              about the causes, but I'm still a bit mystified.
              > It seems one memory leak problem deals with replacing DOM nodes
              that have event handlers already attached. Another problem deals
              with functions declared within functions. It looks as if most of
              Yahoo's custom classes have this property.
              > Does Yahoo have any suggestions on how to deal with these issues,
              since I'm assuming they must be handled by any AJAX type web
              application?
              > Thanks for any help in advance, and I've been very pleased with
              the library!
              > Randall T.
              >
            Your message has been successfully submitted and would be delivered to recipients shortly.