摘要:前言在谈内存泄漏这个问题之前先看看的垃圾收集机制,具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存。闭包不会引起内存泄漏由于之前的版本对对象和对象使用不同的垃圾收集。因此闭包在的这些版本中会导致一些特殊的问题。
前言
在谈内存泄漏这个问题之前先看看JavaScript的垃圾收集机制,JavaScript 具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间)。常用的的方法有两种,即标记清楚和引用计数。
1. 标记清除
JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep)。垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
1. 引用计数
引用计数(reference counting)的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
Netscape Navigator 3.0 是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题,请看下面这个例子:
function problem(){ var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA; }
说明:objectA 和objectB 通过各自的属性相互引用,即这两个对象的引用次数都是2,在采用标记清除策略的实现中,由于函数执行之后,这两个对象都离开了作用域,因此这种相互引用不是个问题。但在采用引用计数策略的实现中,当函数执行完毕后,objectA 和objectB 还说明将继续存在,因为它们的引用次数永远不会是0。假如这个函数被重复多次调用,就会导致大量内存得不到回收。
为此,Netscape 在Navigator 4.0 中放弃了引用计数方式,然而引用计数导致的麻烦并未就此了结。IE9以前中有一部分对象并不是原生JavaScript 对象。例如,其BOM 和DOM 中的对象就是使用C++以COM(Component Object Model,组件对象模型)对象的形式实现的,而COM 对象的垃圾收集机制采用的就是引用计数策略。因此,即使IE 的JavaScript 引擎是使用标记清除策略来实现的,但JavaScript 访问的COM 对象依然是基于引用计数策略的。换句话说,只要在IE 中涉及COM 对象,就会存在循环引用的问题。
比如:
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.element = element; element.someObject = myObject;
DOM 元素(element)与一个原生JavaScript 对象(myObject)之间创建了循环引用。其中,变量myObject 有一个名为element 的属性指向element 对象;而变量element 也有一个属性名叫someObject 回指myObject。由于存在这个循环引用,即使将例子中的DOM 从页面中移除,它也永远不会被回收。
解决办法:将变量设为null从而切断变量与它此前引用的值之间的连接。
myObject.element = null; element.someObject = null;
看完上面的内容,我来谈正题。
闭包不会引起内存泄漏由于IE9 之前的版本对JScript 对象和COM 对象使用不同的垃圾收集。因此闭包在IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁
请看例子:
function assignHandler(){ var element = document.getElementById("someElement"); element.onclick = function(){ alert(element.id); }; }
以上代码创建了一个作为element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用。由于匿名函数保存了一个对assignHandler()的活动对象的引用,因此就会导致无法减少element 的引用数。只要匿名函数存在,element 的引用数至少也是1,因此它所占用的内存就永远不会被回收
解决办法前言已经提到过,把element.id 的一个副本保存在一个变量中,从而消除闭包中该变量的循环引用同时将element变量设为null。
function assignHandler(){ var element = document.getElementById("someElement"); var id = element.id; element.onclick = function(){ alert(id); }; element = null; }
总结:闭包并不会引起内存泄漏,只是由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集,从而导致内存无法进行回收,这是IE的问题,所以闭包和内存泄漏没半毛钱关系。
这篇文章里做了详细的测试,有兴趣的可以点击查看
小小插曲:发个群链接,有兴趣的可以加入交流,群号:519875573
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/91124.html
摘要:解决方式是,当我们不使用它们的时候,手动切断链接淘汰把和对象转为了真正的对象,避免了使用这种垃圾收集策略,消除了以下常见的内存泄漏的主要原因。以上参考资料高程垃圾收集类内存泄漏及如何避免内存泄露及解决方案详解类内存泄漏及如何避免 showImg(http://ww1.sinaimg.cn/large/005Y4rCogy1ft1ikzcqzqj30ka0et77a.jpg); 前言 起...
摘要:就会造成内存泄漏这里导致一直存在内存中应该将解除引用来避免内存泄漏这里导致一致存在内存中的垃圾回收机制看完内存泄漏需要了解下的垃圾回收机制,首先具有自动垃圾回收机制,会找出不再使用的变量,然后释放其占用的内存。 由闭包引出的垃圾回收 闭包的特性 函数嵌套函数 函数内部可以引用外部的参数和变量 参数和变量不会被垃圾回收机制回收 闭包的定义 闭包 是指有权访问另一个函数作用域中的变量的...
摘要:积少成多,最后造成内存泄漏。前端内存泄漏的影响,都是发生在客户机器上,而且基本上现代浏览器也会做好保护机制,一般自行刷新之后都会解决。但是,一旦后端绘制内存泄漏造成宕机之后,整个服务器都会受影响,危险性更大,搞不好年终奖就没了。 引言 Memory Leak 是最难排查调试的 Bug 种类之一,因为内存泄漏是个 undecidable problem,只有开发者才能明确一块内存是不是需...
摘要:的内存泄漏对于这门语言的使用者来说,大多数的使用者的内存管理意识都不强。内存泄漏的定义指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。 javascript的内存泄漏 对于JavaScript这门语言的使用者来说,大多数的使用者的内存管理意识都不强。因为JavaScript一直以来都只作为在网页上使用的脚本语言,而网页往往都不会长时间的运行,所以使用者对JavaScript的...
摘要:垃圾回收内存管理实践先通过一个来看看在中进行垃圾回收的过程是怎样的内存泄漏识别在环境里提供了方法用来查看当前进程内存使用情况,单位为字节中保存的进程占用的内存部分,包括代码本身栈堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技术栈 | https:...
阅读 1922·2021-10-11 10:59
阅读 1032·2021-09-07 09:59
阅读 2225·2021-08-27 16:17
阅读 2782·2019-08-30 15:54
阅读 2273·2019-08-30 12:58
阅读 1772·2019-08-30 12:53
阅读 1464·2019-08-28 18:13
阅读 732·2019-08-26 13:35