常规循环引用内存泄漏和Closure内存泄漏 一些 简单的解决方案

目前大多数ajax前端的javascript framework都利用对事件的管理,解决了该问题。

如果你需要自己解决这个问题,可以参考以下的一些方法:

  • 可以利用递归Dom树,解除event绑定,从而解除循环引用:

    														
           
           
    if (window.attachEvent) { var clearElementProps = [ 'data', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'ondblclick', 'onclick', 'onselectstart', 'oncontextmenu' ]; window.attachEvent("onunload", function() { var el; for(var d = document.all.length;d--;){ el = document.all[d]; for(var c = clearElementProps.length;c--;){ el[clearElementProps[c]] = null; } } }); }
  • http://novemberborn.net/javascript/event-cache一文中则通过增加EventCache,从而给出一个相对结构化的解决方案


    • /*     EventCache Version 1.0
          Copyright 2005 Mark Wubben

          Provides a way for automagically removing events from nodes and thus preventing memory leakage.
          See <http://novemberborn.net/javascript/event-cache> for more information.
          
          This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
      */

      /*     Implement array.push for browsers which don't support it natively.
          Please remove this if it's already in other code 
      */
      if (Array.prototype.push  ==   null ){
          Array.prototype.push  =   function (){
               for ( var  i  =   0 ; i  <  arguments.length; i ++ ){
                   this [ this .length]  =  arguments[i];
              };
               return   this .length;
          };
      };

      /*     Event Cache uses an anonymous function to create a hidden scope chain.
          This is to prevent scoping issues. 
      */
      var  EventCache  =   function (){
           var  listEvents  =  [];
          
           return  {
              listEvents : listEvents,
          
              add :  function (node, sEventName, fHandler, bCapture){
                  listEvents.push(arguments);
              },
          
              flush :  function (){
                   var  i, item;
                   for (i  =  listEvents.length  -   1 ; i  >=   0 ; i  =  i  -   1 ){
                      item  =  listEvents[i];
                      
                       if (item[ 0 ].removeEventListener){
                          item[ 0 ].removeEventListener(item[ 1 ], item[ 2 ], item[ 3 ]);
                      };
                      
                       /*  From this point on we need the event names to be prefixed with 'on"  */
                       if (item[ 1 ].substring( 0 ,  2 )  !=   " on " ){
                          item[ 1 ]  =   " on "   +  item[ 1 ];
                      };
                      
                       if (item[ 0 ].detachEvent){
                          item[ 0 ].detachEvent(item[ 1 ], item[ 2 ]);
                      };
                      
                      item[ 0 ][item[ 1 ]]  =   null ;
                  };
              }
          };
      }();

    • 使用方法也很简单:

      												
             
             
      <script type="text/javascript">
      function addEvent(oEventTarget, sEventType, fDest){
              if(oEventTarget.attachEvent){
      		oEventTarget.attachEvent("on" + sEventType, fDest);
      	} elseif(oEventTarget.addEventListener){
      		oEventTarget.addEventListener(sEventType, fDest, true); 
      	} elseif(typeof oEventTarget[sEventType] == "function"){
                      var fOld = oEventTarget[sEventType];
      		oEventTarget[sEventType] = function(e){ fOld(e); fDest(e); };
      	} else {
      		oEventTarget[sEventType] = fDest;
      	};
      
      	/* Implementing EventCache for all event systems */
      	EventCache.add(oEventTarget, sEventType, fDest, true);
      };
      
      
      function createLeak(){
               var body = document.body;
      
      	function someHandler(){
      
                     return body;
      
      };

      addEvent(body, "click", someHandler);
      };

      window.onload = function(){
      var i = 500;
      while(i > 0){
      createLeak();
      i = i - 1;
      }
      };

      window.onunload = EventCache.flush;
      </script>



    • http://talideon.com/weblog/2005/03/js-memory-leaks.cfm一文中的方法类似:

      • /*
         * EventManager.js
         * by Keith Gaughan
         *
         * This allows event handlers to be registered unobtrusively, and cleans
         * them up on unload to prevent memory leaks.
         *
         * Copyright (c) Keith Gaughan, 2005.
         *
         * All rights reserved. This program and the accompanying materials
         * are made available under the terms of the Common Public License v1.0
         * (CPL) which accompanies this distribution, and is available at
         * http://www.opensource.org/licenses/cpl.php
         *
         * This software is covered by a modified version of the Common Public License
         * (CPL), where Keith Gaughan is the Agreement Steward, and the licensing
         * agreement is covered by the laws of the Republic of Ireland.
         
        */

        //  For implementations that don't include the push() methods for arrays.
        if  ( ! Array.prototype.push) {
            Array.prototype.push  =   function (elem) {
                 this [ this .length]  =  elem;
            }
        }

        var  EventManager  =  {
            _registry:  null ,

            Initialise:  function () {
                 if  ( this ._registry  ==   null ) {
                     this ._registry  =  [];

                     //  Register the cleanup handler on page unload.
                    EventManager.Add(window,  " unload " ,  this .CleanUp);
                }
            },

             /* *
             * Registers an event and handler with the manager.
             *
             * @param  obj         Object handler will be attached to.
             * @param  type        Name of event handler responds to.
             * @param  fn          Handler function.
             * @param  useCapture  Use event capture. False by default.
             *                     If you don't understand this, ignore it.
             *
             * @return True if handler registered, else false.
             
        */
            Add:  function (obj, type, fn, useCapture) {
                 this .Initialise();

                 //  If a string was passed in, it's an id.
                 if  ( typeof  obj  ==   " string " ) {
                    obj  =  document.getElementById(obj);
                }
                 if  (obj  ==   null   ||  fn  ==   null ) {
                     return   false ;
                }

                 //  Mozilla/W3C listeners?
                 if  (obj.addEventListener) {
                    obj.addEventListener(type, fn, useCapture);
                     this ._registry.push({obj: obj, type: type, fn: fn, useCapture: useCapture});
                     return   true ;
                }

                 //  IE-style listeners?
                 if  (obj.attachEvent  &&  obj.attachEvent( " on "   +  type, fn)) {
                     this ._registry.push({obj: obj, type: type, fn: fn, useCapture:  false });
                     return   true ;
                }

                 return   false ;
            },

             /* *
             * Cleans up all the registered event handlers.
             
        */
            CleanUp:  function () {
                 for  ( var  i  =   0 ; i  <  EventManager._registry.length; i ++ ) {
                     with  (EventManager._registry[i]) {
                         //  Mozilla/W3C listeners?
                         if  (obj.removeEventListener) {
                            obj.removeEventListener(type, fn, useCapture);
                        }
                         //  IE-style listeners?
                         else   if  (obj.detachEvent) {
                            obj.detachEvent( " on "   +  type, fn);
                        }
                    }
                }

                 //  Kill off the registry itself to get rid of the last remaining
                 //  references.
                EventManager._registry  =   null ;
            }
        };


      • 使用起来也很简单

        
                 
                 
        <html>
        <head>
        <script type=text/javascript src=EventManager.js></script>
        <script type=text/javascript>
            function onLoad() {
        
            EventManager.Add(document.getElementById(testCase),click,hit );
        returntrue;
            }
        
            function hit(evt) {
                alert(click);
            }
        </script>
        </head>
        
        <body οnlοad='javascript: onLoad();'>
        
        <div id='testCase' style='width:100%; height: 100%; background-color: yellow;'>
          <h1>Click me!</h1>
        </div>
        
        </body>
        </html>



    • google map api同样提供了一个类似的函数用在页面的unload事件中,解决Closure带来的内存泄露问题。
    • 当然,如果你不嫌麻烦,你也可以为每个和native object有关的就阿vascript object编写一个destoryMemory函数,用来手动调用,从而手动解除Dom对象的事件绑定。

    • 还有一种就是不要那么OO,抛弃Dom的一些特性,用innerHTML代替appendChild,避开循环引用。详细见http://birdshome.cnblogs.com/archive/2005/02/16/104967.html中的讨论贴。\



      其它一些琐碎的注意点

      变量定义一定要用var,否则隐式声明出来的变量都是全局变量,不是局部变量;
      全局变量没用时记得要置null;
      注意正确使用delete,删除没用的一些函数属性;
      注意正确使用try...cache,确保去处无效引用的代码能被正确执行;
      open出来的窗口即使close了,它的window对象还是存在的,要记得删除引用;
      frame和iframe的情况和窗口的情况类似。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
闭包可以造成内存泄露是因为闭包会持有对外部函数作用域的引用,导致外部函数中的变量无法被垃圾回收机制回收,从而占用了额外的内存空间。 当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量时,就形成了闭包。当外部函数执行完毕后,其作用域中的变量理论上应该被销毁,但由于内部函数仍然引用着这些变量,导致这些变量无法被垃圾回收机制回收。 以下是一个示例代码,展示了闭包如何造成内存泄露: ```javascript function createClosure() { var data = "Hello"; // 外部函数的变量 return function() { console.log(data); // 内部函数引用了外部函数的变量 }; } var closure = createClosure(); // 创建闭包 closure(); // 输出 "Hello" // 由于闭包中引用了外部函数的变量,导致外部函数的作用域无法被销毁 ``` 在上述代码中,`createClosure`函数返回了一个内部函数,该内部函数引用了`data`变量。即使`createClosure`函数执行完毕后,`data`变量仍然被内部函数引用着,无法被垃圾回收机制回收,从而造成了内存泄露。 为了避免闭包造成内存泄露,可以采取以下几种方式: 1. 尽量避免使用闭包,特别是在处理大量数据时。 2. 在不需要使用闭包时,及时释放对外部函数作用域的引用。 3. 使用`let`关键字代替`var`关键字来声明变量,因为`let`具有块级作用域,可以更容易地释放内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值