javascript内存泄漏知识点总结

来源:互联网 发布:电脑上淘宝怎么发链接 编辑:程序博客网 时间:2024/05/29 16:49

本博文将从javascript的垃圾回收机制说起,接下来则介绍关于javascript内存泄漏涉及到的各个知识点,包括什么是内存泄漏、内存泄漏的原因、管理内存等

1 JavaScript的垃圾回收机制

JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。而在C和C++之类的语言中,开发人员的一项基本任务就是手工跟踪内存的使用情况,这是造成许多问题的一个根源。在编写JavaScript程序时,开发人员不用再关心内存的使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。
浏览器实现垃圾收集机制的策略通常有两个:标记清除和引用计数

1.1 标记清除

JavaScript中常见的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能用到它们。而当变量离开环境时,这将其 标记为“离开环境”。
垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
到目前为止,IE、Firefox、Opera、chrome和Safari的JavaScript实现使用的都是标记清除式的垃圾收集策略(或类似的策略),只不过垃圾收集的时间间隔互有不同。

1.2 引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。

    Netscape Navigator3是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。
function problem() {
 var objectA = new Object();
var objectB = new Object();
 objectA.someOtherObject = objectB;
 objectA.someOtherObject = objectB;
 objectB.anotherObject = objectA;
    }
  以上代码a和b的引用次数都是2,problem()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为objectA和objectB的引用次数不为0,所以不会被垃圾回收器回收内存,如果problem函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。
   我们知道,IE中有一部分对象并不是原生js对象。例如,其内存泄露DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。
    因为在 IE7、IE8 中DOM和BOM中的对象就是使用C++以COM对象的形式实现的,所以在IE7/8 中DOM对象或者ActiveX对象循环引用导致内存泄漏
  IE9+并不存在循环引用导致Dom内存泄露问题,可能是微软做了优化,或者Dom的回收方式已经改变

2 什么是内存泄漏


程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)


3  内存泄漏的原因

   全局变量引起的内存泄漏
function leaks(){          leak = 'xxxxxx';//leak 成为一个全局变量,不会被回收        }

闭包引起的内存泄漏

var leaks = (function(){      var leak = 'xxxxxx';// 被闭包所引用,不会被回收    return function(){                    console.log(leak);    }})()

dom清空或删除时,事件未清除导致的内存泄漏

<div id="container">  </div>$('#container').bind('click', function(){    console.log('click');}).remove();// zepto 和原生 js下,#container dom 元素,还在内存里jquery 的 emptyremove会帮助开发者避免这个问题
</div>$('#container').bind('click', function(){    console.log('click');}).remove();// zepto 和原生 js下,#container dom 元素,还在内存里jquery 的 emptyremove会帮助开发者避免这个问题
$('#container').bind('click', function(){    console.log('click');}).remove();// zepto 和原生 js下,#container dom 元素,还在内存里jquery 的 emptyremove会帮助开发者避免这个问题
    console.log('click');}).remove();// zepto 和原生 js下,#container dom 元素,还在内存里jquery 的 emptyremove会帮助开发者避免这个问题
}).remove();// zepto 和原生 js下,#container dom 元素,还在内存里jquery 的 emptyremove会帮助开发者避免这个问题
// zepto 和原生 js下,#container dom 元素,还在内存里jquery 的 emptyremove会帮助开发者避免这个问题
<div id="container">  </div>$('#container').bind('click', function(){    console.log('click');}).off('click').remove();//把事件清除了,即可从内存中移除
</div>$('#container').bind('click', function(){    console.log('click');}).off('click').remove();//把事件清除了,即可从内存中移除
$('#container').bind('click', function(){    console.log('click');}).off('click').remove();//把事件清除了,即可从内存中移除
    console.log('click');}).off('click').remove();//把事件清除了,即可从内存中移除
}).off('click').remove();//把事件清除了,即可从内存中移除
//把事件清除了,即可从内存中移除







原创粉丝点击