前言

在谈内存泄漏这个问题之前先看看JavaScript的垃圾收集机制,JavaScript 具有自动垃圾收集机制,就是找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间)。常用的的方法有两种,即标记清楚和引用计数。

1. 标记清除

JavaScript 中最常用的垃圾收集方式是标记清除(mark-and-sweep)。垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

2. 引用计数

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

Netscape Navigator 3.0 是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题,请看下面这个例子:

  1. function problem(){
  2. var objectA = new Object();
  3. var objectB = new Object();
  4. objectA.someOtherObject = objectB;
  5. objectB.anotherObject = objectA;
  6. }

说明: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 对象,就会存在循环引用的问题。

比如:

  1. var element = document.getElementById("some_element");
  2. var myObject = new Object();
  3. myObject.element = element;
  4. element.someObject = myObject;

DOM 元素(element)与一个原生JavaScript 对象(myObject)之间创建了循环引用。其中,变量myObject 有一个名为element 的属性指向element 对象;而变量element 也有一个属性名叫someObject 回指myObject。由于存在这个循环引用,即使将例子中的DOM 从页面中移除,它也永远不会被回收。

解决办法:将变量设为null从而切断变量与它此前引用的值之间的连接。

  1. myObject.element = null;
  2. element.someObject = null;

看完上面的内容,我来谈正题。

闭包不会引起内存泄漏

由于IE9 之前的版本对JScript 对象和COM 对象使用不同的垃圾收集。因此闭包在IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁请看例子:

  1. function assignHandler(){
  2. var element = document.getElementById("someElement");
  3. element.onclick = function(){
  4. alert(element.id);
  5. };
  6. }

以上代码创建了一个作为element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用。由于匿名函数保存了一个对assignHandler()的活动对象的引用,因此就会导致无法减少element 的引用数。只要匿名函数存在,element 的引用数至少也是1,因此它所占用的内存就永远不会被回收

解决办法前言已经提到过,把element.id 的一个副本保存在一个变量中,从而消除闭包中该变量的循环引用同时将element变量设为null。

  1. function assignHandler(){
  2. var element = document.getElementById("someElement");
  3. var id = element.id;
  4. element.onclick = function(){
  5. alert(id);
  6. };
  7. element = null;
  8. }

总结:闭包并不会引起内存泄漏,只是由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集,从而导致内存无法进行回收,这是IE的问题,所以闭包和内存泄漏没半毛钱关系。

作者:yancyenough

来源:51CTO

闭包会造成内存泄漏吗?相关推荐

  1. javascript闭包产生的内存泄漏

    使用时,最好不要使用闭包,可以将函数声明后,直接调用函数名即可. <html><head><script type="text/javascript" ...

  2. 解析面试常问题之JavaScript中的闭包概念及应用,顺便普及一下大家口中常说的内存泄漏问题

    JavaScript中的闭包是一个面试中经常被考到的问题,大家可能都对这个概念多多少少都有一些模糊的概念或者一点都不了解,那么今天就来给大家讲解一下. 公众号:前端印象 不定时有送书活动,记得关注~ ...

  3. 闭包为什么会造成内存泄漏?

    本文基于<JavaScript高级程序设计>整理. 闭包概念: 闭包是一类函数.哪一类?有权访问 另一个 函数 作用域中变量的函数. 想要理解闭包,必须从理解函数被[调用]的时候会发生什么 ...

  4. js垃圾回收机制和引起内存泄漏的操作

    Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. JS中最常见的垃圾回收方式是标记清除. 工作原理:是当变量进入环境时,将这个变量标记为"进入环境".当变量离 ...

  5. 有意思的 Node.js 内存泄漏问题

    作者:elvinpeng,腾讯 WXG 前端开发工程师 Node.js 使用的是 V8 引擎,会自动进行垃圾回收(Garbage Collection,GC),因而写代码的时候不需要像 C/C++ 一 ...

  6. 设置log缓存_全局变量、事件绑定、缓存爆炸?Node.js内存泄漏问题分析

    作者:elvinpeng,腾讯 WXG 前端开发工程师 Node.js 使用的是 V8 引擎,会自动进行垃圾回收(Garbage Collection,GC),因而写代码的时候不需要像 C/C++ 一 ...

  7. 一文带你了解如何排查内存泄漏导致的页面卡顿现象

    作者 | 零一0101       责编 | 欧阳姝黎 不知道在座的各位有没有被问到过这样一个问题:如果页面卡顿,你觉得可能是什么原因造成的?有什么办法锁定原因并解决吗? 这是一个非常宽泛而又有深度的 ...

  8. JavaScript中内存溢出和内存泄漏

    内存溢出 定义变量占用内存空间超过系统内存空间导致报错,可通过字面量理解,例如没有清理的闭包,定时器,全局变量 内存泄漏 占用的内存没有及时释放,内存泄漏多了就会导致内存溢出,例如for循环中 var ...

  9. JS中常见的内存泄漏及识别方式

    JavaScript常见的内存泄漏及识别方式 1.什么是内存 2.什么是内存泄漏 3.内存泄漏导致的后果 4.常见的内存泄漏 (1)全局变量引起的内存泄漏 (2)闭包引起的内存泄漏 (3)被遗忘的定时 ...

最新文章

  1. 快速在PowerPoint文档中添加图表
  2. 阿里云边缘云全新架构升级,助力CDN操控新体验
  3. 嘘!阿里程序媛上班的第一件事是...
  4. 在 Java EE 组件中使用 Camel Routes
  5. 210228Linux 条件变量 线程池
  6. 从零开始使用CodeArt实践最佳领域驱动开发(三)
  7. Python获取文件后缀名
  8. Ant编译SWF、SWC例子脚本
  9. C代码工具--自动生成enum值和名字映射代码
  10. eclipse import的项目报autowired cannot be resolved to a type的错误
  11. CAM350 V14.5安装记录
  12. u盘修复计算机系统,用u盘修复win7系统
  13. 2060显卡驱动最新版本_如何更新你的显卡驱动程序
  14. 【JAVA SE基础篇】27.面向对象三大特征之封装
  15. Python|动态规划问题--斐波那契数列
  16. 修改MAC密码 Navicat每次打开都要输入密码
  17. java之21点游戏(只包含一个玩家和一个庄家,是否抓牌,没有黑杰克)
  18. 对比两个自定义对象是否相等
  19. 三星typec转接耳机没反应_typec转3.5mm转接线,你买对了吗?
  20. 从零开始学 Web 之 移动Web(九)微金所案例

热门文章

  1. 细聊分布式ID生成方法-2
  2. 读取寄存器值_温湿度传感器与S7-1200 PLC通讯读取温湿度案例
  3. 华为交换机重制_华为交换机重置命令
  4. java 导出es数据_elasticseach 数据的导出与导出工具elasticdump使用
  5. android屏幕共享实现方式,基于WebRtc在Android端实现屏幕共享
  6. springboot幂等性_Spring Boot + Redis + 注解 + 拦截器来实现接口幂等性校验
  7. mysql根用户的密码是什么_Mysql忘记根用户密码 怎么办?
  8. ES6之let能替代var吗?
  9. 闽南师范大学计算机学院研究生,闽南师范大学计算机学院2019考研调剂信息
  10. java中int边界值_数组中重复的数字2019.12.06