js内存泄露的几种情况详细探讨


Posted in Javascript onMay 31, 2013

内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。在C++中,因为是手动管理内存,内存泄露是经常出现的事情。而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄露。浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。

1、当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。

<div id="myDiv"> 
<input type="button" value="Click me" id="myBtn"> 
</div> 
<script type="text/javascript"> 
var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
document.getElementById("myDiv").innerHTML = "Processing..."; 
} 
</script>

应改成下面
<div id="myDiv"> 
<input type="button" value="Click me" id="myBtn"> 
</div> 
<script type="text/javascript"> 
var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
btn.onclick = null; 
document.getElementById("myDiv").innerHTML = "Processing..."; 
} 
</script>

或者采用事件委托
<div id="myDiv"> 
<input type="button" value="Click me" id="myBtn"> 
</div> 
<script type="text/javascript"> 
document.onclick = function(event){ 
event = event || window.event; 
if(event.target.id == "myBtn"){ 
document.getElementById("myDiv").innerHTML = "Processing..."; 
} 
} 
</script>

2、
var a=document.getElementById("#xx"); 
var b=document.getElementById("#xxx"); 
a.r=b; 
b.r=a;

var a=document.getElementById("#xx"); 
a.r=a;

对于纯粹的 ECMAScript 对象而言,只要没有其他对象引用对象 a、b,也就是说它们只是相互之间的引用,那么仍然会被垃圾收集系统识别并处理。但是,在 Internet Explorer 中,如果循环引用中的任何对象是 DOM 节点或者 ActiveX 对象,垃圾收集系统则不会发现它们之间的循环关系与系统中的其他对象是隔离的并释放它们。最终它们将被保留在内存中,直到浏览器关闭。
3、
var elem = document.getElementById('test'); 
elem.addEventListener('click', function() { 
alert('You clicked ' + elem.tagName); 
});

这段代码把一个匿名函数注册为一个DOM结点的click事件处理函数,函数内引用了一个DOM对象elem,就形成了闭包。这就会产生一个循环引用,即:DOM->闭包->DOM->闭包...DOM对象在闭包释放之前不会被释放;而闭包作为DOM对象的事件处理函数存在,所以在DOM对象释放前闭包不会释放,即使DOM对象在DOM tree中删除,由于这个循环引用的存在,DOM对象和闭包都不会被释放。可以用下面的方法可以避免这种内存泄露
var elem = document.getElementById('test'); 
elem.addEventListener('click', function() { 
alert('You clicked ' + this.tagName); // 不再直接引用elem变量 
});

4、
function bindEvent() 
{ 
var obj=document.createElement("XXX"); 
obj.onclick=function(){ 
//Even if it's a empty function 
} 
}

闭包非常容易构成循环引用。如果一个构成闭包的函数对象被指定给,比如一个 DOM 节点的事件处理器,而对该节点的引用又被指定给函数对象作用域中的一个活动(或可变)对象,那么就存在一个循环引用。
DOM_Node.onevent -<function_object.[[scope]] -<scope_chain -<Activation_object.nodeRef -<DOM_Node。

形成这样一个循环引用是轻而易举的,而且稍微浏览一下包含类似循环引用代码的网站(通常会出现在网站的每个页面中),就会消耗大量(甚至全部)系统内存。
解决之道,将事件处理函数定义在外部,解除闭包

function bindEvent() 
{ 
var obj=document.createElement("XXX"); 
obj.onclick=onclickHandler; 
} 
function onclickHandler(){ 
//do something 
}

或者在定义事件处理函数的外部函数中,删除对dom的引用(题外,《JavaScript权威指南》中介绍过,闭包中,作用域中没用的属性可以删除,以减少内存消耗。)
function bindEvent() 
{ 
var obj=document.createElement("XXX"); 
obj.onclick=function(){ 
//Even if it's a empty function 
} 
obj=null; 
}

5、
a = {p: {x: 1}}; 
b = a.p; 
delete a.p;

执行这段代码之后b.x的值依然是1.由于已经删除的属性引用依然存在,因此在JavaScript的某些实现中,可能因为这种不严谨的代码而造成内存泄露。所以在销毁对象的时候,要遍历属性中属性,依次删除。
6. 自动类型装箱转换
别不相信,下面的代码在ie系列中会导致内存泄露
var s=”lalala”; 
alert(s.length);

s本身是一个string而非object,它没有length属性,所以当访问length时,JS引擎会自动创建一个临时String对象封装s,而这个对象一定会泄露。这个bug匪夷所思,所幸解决起来相当容易,记得所有值类型做.运算之前先显式转换一下:
var s="lalala"; 
alert(new String(s).length);

7、某些DOM操作
IE系列的特有问题 简单的来说就是在向不在DOM树上的DOM元素appendChild;IE7中,貌似为了改善内存泄露,IE7采用了极端的解决方案:离开页面时回收所有DOM树上的元素,其它一概不管。
Javascript 相关文章推荐
JavaScript 中的replace方法说明
Apr 13 Javascript
jQuery技巧大放送 学习jquery的朋友可以看下
Oct 14 Javascript
javascript 鼠标拖动图标技术
Feb 07 Javascript
在百度知道团队中快速审批新成员的js脚本
Feb 02 Javascript
js取得html iframe中的元素和变量值
Jun 30 Javascript
jQuery绑定自定义事件的魔法升级版
Jun 30 Javascript
浅谈JavaScript 数据属性和访问器属性
Sep 01 Javascript
webpack配置打包后图片路径出错的解决
Apr 26 Javascript
解决Layui 表单提交数据为空的问题
Aug 15 Javascript
elementUI 设置input的只读或禁用的方法
Oct 30 Javascript
layui.use模块外部使用其内部定义的js封装函数方法
Sep 16 Javascript
PHP读取远程txt文档到数组并实现遍历
Aug 25 Javascript
js判断鼠标同时离开两个div的思路及代码
May 31 #Javascript
Javacript实现颜色梯度变化和渐变的效果代码
May 31 #Javascript
js 输出内容到新窗口具体实现代码
May 31 #Javascript
chrome浏览器不支持onmouseleave事件的解决技巧
May 31 #Javascript
window.event.keyCode兼容IE和Firefox实现js代码
May 30 #Javascript
js怎么终止程序return不行换jfslk
May 30 #Javascript
javascript-表格排序(降序/反序)实现介绍(附图)
May 30 #Javascript
You might like
Syphon 虹吸式咖啡壶冲煮–拨动法
2021/03/03 冲泡冲煮
PHP setcookie指定domain参数后,在IE下设置cookie失效的解决方法
2011/09/09 PHP
一个简单的PHP验证码实现代码
2014/05/10 PHP
PHP获取中英混合字符串长度的方法
2014/06/07 PHP
php实现的用户查询类实例
2015/06/18 PHP
PHP如何将XML转成数组
2016/04/04 PHP
PHP面向对象自动加载机制原理与用法分析
2016/10/14 PHP
php实现留言板功能(代码详解)
2017/03/28 PHP
PHP使用SMTP邮件服务器发送邮件示例
2018/08/28 PHP
PHP中引用类型和值类型功能与用法示例
2019/02/26 PHP
jQuery源码中的chunker 正则过滤符分析
2012/07/31 Javascript
这些年、我收集的JQuery代码小结
2012/08/01 Javascript
jquery.validate使用详解
2016/06/02 Javascript
jQuery实现的粘性滚动导航栏效果实例【附源码下载】
2017/10/19 jQuery
vue-cli webpack模板项目搭建及打包时路径问题的解决方法
2018/02/26 Javascript
JavaScript获取移动设备型号的实现代码(JS获取手机型号和系统)
2018/03/10 Javascript
vue webpack开发访问后台接口全局配置的方法
2018/09/18 Javascript
JavaScript基于遍历操作实现对象深拷贝功能示例
2019/03/05 Javascript
JavaScript实现简单计算器功能
2019/12/19 Javascript
JS前端面试必备——基本排序算法原理与实现方法详解【插入/选择/归并/冒泡/快速排序】
2020/02/24 Javascript
jQuery实现点击滚动到指定元素上的方法分析
2020/03/19 jQuery
antd-DatePicker组件获取时间值,及相关设置方式
2020/10/27 Javascript
Python的函数的一些高阶特性
2015/04/27 Python
Python2和Python3中print的用法示例总结
2017/10/25 Python
Pycharm在创建py文件时,自动添加文件头注释的实例
2018/05/07 Python
python消费kafka数据批量插入到es的方法
2018/12/27 Python
Python 列表的清空方式
2020/01/13 Python
Tensorflow中批量读取数据的案列分析及TFRecord文件的打包与读取
2020/06/30 Python
CSS3实现的炫酷菜单代码分享
2015/03/12 HTML / CSS
HTML5进阶段内联标签汇总(小篇)
2016/07/13 HTML / CSS
Python中如何定义一个函数
2016/09/06 面试题
网络工程专业毕业生推荐信
2013/10/28 职场文书
证券期货行业个人的自我评价
2013/12/26 职场文书
Java新手教程之ArrayList的基本使用
2021/06/20 Java/Android
基于Python实现一个春节倒计时脚本
2022/01/22 Python
JavaScript展开运算符和剩余运算符的区别详解
2022/02/18 Javascript