• Concept

      • WHAT :

        • 内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。正常情况下,垃圾回收器在DOM元素和event处理器不被引用或访问的时候回收它们。但是,IE的早些版本(IE7和之前)中内存泄漏是很容易出现的,因为内存管理器不能正确理解Javascript生命周期而且在周期被打破(可以通过赋值为null实现)前不会回收内存。

           
      • WHY :
        • 在大型Web应用程序中内存泄漏是一种常见的意外编程错误。内存泄漏会降低Web应用程序的性能,直到浪费的内存超过了系统所能分配的,应用程序将不能使用。作为一Web开发者,开发一个满足功能要求的应用程序只是第一步,性能要求和Web应用程序的成功是同样重要的,更何况它可能会导致应用程序错误或浏览器崩溃。
    • Quick Review
      • HOW:

        • 不同的浏览器中存在各种内存泄露方式,目前发现的主要是这样几种:
        1. 循环引用:已经确认存在泄漏的浏览器:IE6.0 FF2.0

          • 含有DOM对象的循环引用将导致大部分当前主流浏览器内存泄露 这里有两个简单的概念

            • 引用:a.属性=b,a就引用了b
            • 循环引用:简单来说假如a引用了b,b又引用了a,a和b就构成了循环引用。
              1. a和b循环引用:
                var a=new Object;
                var b=new Object;
                a.r=b;
                b.r=a;
              2. a循环引用自己:
                var a=new Object;
                a.r=a;
          • 循环引用很常见且大部分情况下是无害的,但当参与循环引用的对象中有DOM对象或者ActiveX对象时,循环引用将导致内存泄露。我们把例子中的任何一个new Object替换成document.getElementById或者document.createElement就会发生内存泄露了。尽管这看起来非常容易理解,但是因为有closure的参与而使事情变得复杂,有些closure导致的循环引用很难被察觉。下面是一个非常常见的动态绑定事件:
            • function bindEvent() 

                  var obj=document.createElement("XXX"); 
                  obj.onclick=function(){ 
                      //Even if it's a empty function 
                  } 
              }
            • 这个bindEvent执行时100%会发生内存泄露,Someone 可能会问,哪里出现了循环引用? 关于closure和scope chain参与的循环引用比较复杂,此处暂不深入讨论。有一个简单的判断方式:函数将间接引用所有它能访问的对象。obj.onclick这个函数中 可以访问外部的变量obj 所以他引用了obj,而obj又引用了它,因此这个事件绑定将会造成内存泄露。在IBM的文章中介绍了2种方式解决类似的问题一个是obj=null,另一个是把onclick的函数写在bindEvent外。简单贴下代码:
              1. function bindEvent() 

                    var obj=document.createElement("XXX"); 
                    obj.onclick=onclickHandler; 

                function onclickHandler(){ 
                    //do something 
                }
              2. function bindEvent() 

                    var obj=document.createElement("XXX"); 
                    obj.onclick=function(){ 
                        //Even if it's a empty function 
                    } 
                    obj=null; 
                }
            • 这两个方法都打断了循环引用,可以解决问题,但是似乎对代码表达能力造成了一定破坏,假设有这么一个问题:
              • function bindEvent( ) 

                    var obj = document.createElement("XXX"); 
                    var var0 = "str_0”;   //Here is a variable 
                    obj.onclick=function( ){ 
                        alert(var0);    // I want to visit var here! 
                    } 
                    return obj;   // bindEvent must return obj!
                }
            • 好了,这下两种办法都不行了,假如我把函数写外面去,var0肯定访问不了,假如我把obj弄成null,还怎么return它呢?这并不是空想的需要,这实际 上是一个用JS定制DOM控件的简单抽象:创建DOM元素、设置私有属性、绑定事件。所以,我们必须update一下两个方法。首先,方法1,为了让函数 能访问某些变量,我们可以通过一个Builder函数来订制onclick的外部闭包:
              1.   function bindEvent() 

                    var obj=document.createElement("XXX"); 
                    var var0="OOXX";//Here is a variable 
                    obj.onclick= onclickBuilder(var0);//想访问谁就把谁传进去!! 
                    return obj;//bindEvent must return obj! 

                function onclickBuilder(var0)//这里跟上面对应上就行了 最好参数名字也对应上 

                    return function(){ 
                        alert(var0); 
                    } 
                }
              2. 第二个办法,让obj=null在return 之后执行。
                • function bindEvent()
                  {
                  try{
                  var obj=document.createElement("XXX");
                  var var0="OOXX";//Here is a variable
                  obj.onclick=function(){
                  alert(var0);//I want to visit var2 here!
                  }
                  return obj;//bindEvent must return obj!
                  } finally {
                  obj=null;
                  }
                  }
        2. 某些DOM操作
          • 这是IE系列的特有问题 简单的来说就是在向不在DOM树上的DOM元素appendChild,可能会发生内存泄露(只是可能,具体原因不明,似乎十分复杂,下面例子中去掉onClick也可以避免泄露)。所以appendChild的顺序可能影响内存泄露:

            •   <html> 
                  <head> 
                      <script language="JScript"> 
                      function LeakMemory() 
                      { 
                          var hostElement = document.getElementById("hostElement"); 
                          // Do it a lot, look at Task Manager for memory response 
                          for(i = 0; i < 5000; i++) 
                          { 
                              var parentDiv = 
                                  document.createElement("<div onClick='foo()'>"); 
                              var childDiv = 
                                  document.createElement("<div onClick='foo()'>"); 
                              // This will leak a temporary object 
                              parentDiv.appendChild(childDiv); 
                              hostElement.appendChild(parentDiv); 
                              hostElement.removeChild(parentDiv); 
                              parentDiv.removeChild(childDiv); 
                              parentDiv = null; 
                              childDiv = null; 
                          } 
                          hostElement = null; 
                      }

              function CleanMemory() 
                      { 
                          var hostElement = document.getElementById("hostElement"); 
                          // Do it a lot, look at Task Manager for memory response 
                          for(i = 0; i < 5000; i++) 
                          { 
                              var parentDiv = 
                                  document.createElement("<div onClick='foo()'>"); 
                              var childDiv = 
                                  document.createElement("<div onClick='foo()'>"); 
                              // Changing the order is important, this won't leak 
                              hostElement.appendChild(parentDiv); 
                             parentDiv.appendChild(childDiv); 
                             parentDiv.removeChild(childDiv); 
                              hostElement.removeChild(parentDiv);
                              parentDiv = null; 
                              childDiv = null; 
                          } 
                          hostElement = null; 
                      } 
                      </script> 
                  </head> 
                  <body> 
                      <button onclick="LeakMemory()">Memory Leaking Insert</button> 
                      <button onclick="CleanMemory()">Clean Insert</button> 
                      <div id="hostElement"></div> 
                  </body> 
              </html> 

            • 而在IE7中,貌似为了改善内存泄露,IE7采用了极端的解决方案:离开页面时回收所有DOM树上的元素,其它一概不管。但是这不仅没起到任何作用,反而 使问题变得更加复杂。对这类问题,除了自觉一点绕开这些恶心的东西,多用innerHTML这种无用的建议之外。我想可以通过覆盖 document.createElement来略为改善:

              首先我们定义一个看不见的元素当作垃圾箱,所有新创建的元素都扔进垃圾箱里,这样保证了所有DOM元素都在DOM树上,IE7就可以正确回收了,另一方面也能避免所谓的”appendChild顺序不对导致内存泄露”。
              •   function MemoryFix(){ 
                    var garbageBox=document.createElement("div"); 
                    garbageBox.style.display="none"; 
                    document.body.appendChild(garbageBox); 
                    var createElement=document.createElement; 
                    document.createElement=function(){ 
                        var obj=Function.prototype.apply.apply(createElement,[document,arguments]); 
                        garbageBox.appendChild(obj); 
                        return obj; 
                    } 
                }
        3. 自动类型装箱转换
          • 下面的代码在ie系列中会导致内存泄露:
            • var s=”lalala”;
              alert(s.length);
            • s本身是一个string而非object,它没有length属性,所以当访问length时,JS引擎会自动创建一个临时String对象封装s,而这个对象一定会泄露。这个bug匪夷所思,所幸解决起来相当容易,记得所有值类型做.运算之前先显式转换一下:

              var s="lalala";

              alert(new String(s).length); 

浅谈 JS 内存泄露方式与避免方法(二)的更多相关文章

  1. 浅谈Java内存泄露

    一.引言 先等等吧……累了

  2. 浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

    浅谈JS中的!=.== .!==.===的用法和区别   var num = 1;     var str = '1';     var test = 1;     test == num  //tr ...

  3. 浅谈JS之AJAX

    0x00:什么是Ajax? Ajax是Asynchronous Javascript And Xml 的缩写(异步javascript及xml),Ajax是使用javascript在浏览器后台操作HT ...

  4. 浅谈JS中的闭包

    浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...

  5. 浅谈JS面向对象

    浅谈JS面向对象 一 .什么是面向过程 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了.注重代码的过程部分. 二.什么是面向对象 最先出现在管理学 ...

  6. 浅谈js拖拽

    本文来自网易云社区 作者:刘凌阳 前言 本文依据半年前本人的分享<浅谈js拖拽>撰写,算是一篇迟到的文章. 基本思路 虽然现在关于拖拽的组件库到处都是,HTML5也把拖放纳入了标准.但考虑 ...

  7. 浅谈JS中 var let const 变量声明

    浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...

  8. JS内存泄露常见原因

    详细内容请点击 分享的笔记本-前端 开发中,我们常遇见的一些关于js内存泄露的问题,有时候我们常常会找半天找不出原因,这里给大家介绍简单便捷的方法 1.闭包上下文绑定后没有释放:   2.观察者模式在 ...

  9. Js内存泄露问题总结

    最近接受了一个Js职位的面试,问了很多Js的高级特性,才发现长时间使用已知的特性进行开发而忽略了对这门语言循序渐进的理解,包括Java我想也是一样,偶尔在Sun官方看到JDK6.0列举出来的new f ...

随机推荐

  1. 档案 & 权限管理

    1. owner, group, others root 是万能的天神 权限 连结档 owner group 容量 修改日期 档名 2. chown, chgrp, chmod chgrp –R ro ...

  2. 本地连接linux虚拟机的方法

    1.给虚拟机添加一个ehtX: 2. 3.添加一个ethX 4. 5.设置使用的是哪一个网卡,ifconfig的时候变会得到相应的ip 6. 再在cmd那ping ifconfig出来的自动获取的ip ...

  3. 【BZOJ】1827: [Usaco2010 Mar]gather 奶牛大集会(树形dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1827 仔细想想就好了,, 每个点维护两个值,一个是子树的费用,一个是除了子树和自己的费用.都可以用d ...

  4. 使用StringTokenizer分解字符串

    Java切割字符串.一般使用substring.split.StringTokenizer来处理,前两种是String对象的方法,使用字符串能够直接处理,本文介绍下StringTokenizer的使用 ...

  5. 【python】模块测试 if name main

    verbose=1 def listing(module): if verbose: print '-'*30 print 'name:',module.__name__,'file:',module ...

  6. Android中的ACCESS_MOCK_LOCATION权限使用Demo

    转载地址:http://mobiarch.wordpress.com/2012/07/17/testing-with-mock-location-data-in-android/ The DDMS t ...

  7. python bottle学习(三)动态路由配置(通配符)

    from bottle import (run, route, get, post, default_app, Bottle) @route('/', method='GET') @route('/i ...

  8. sql 循环表中记录

    =========================================================================循环排序查询数据=================== ...

  9. 修改sudoers权限之后无法sudo的最简单解决方法

    网上百度一下进入recovery模式或是单用户模式仍然修改不了sudoers的权限, 后来终于在网上找到了一种最简单的方法,那就是 pkexec chmod 0440 /etc/sudoers

  10. bootstrap 媒体查询

    //各类设备的分辨率 /*超小设备(手机,小于768px)*/ /* Bootstrap 中默认情况下没有媒体查询 */ /*超小型设备(小于768px)*/ @media (min-width:@s ...