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 相关文章推荐
Js 冒泡事件阻止实现代码
Jan 27 Javascript
基于javascript滚动图片具体实现
Nov 18 Javascript
window.open 以post方式传递参数示例代码
Feb 27 Javascript
详解jquery中$.ajax方法提交表单
Nov 03 Javascript
基于jquery实现弹幕效果
Sep 29 Javascript
利用VUE框架,实现列表分页功能示例代码
Jan 12 Javascript
对angular2中的ngfor和ngif指令嵌套实例讲解
Sep 12 Javascript
bootstrap Table实现合并相同行
Jul 19 Javascript
Smartour 让网页导览变得更简单(推荐)
Jul 19 Javascript
解决Vue router-link绑定事件不生效的问题
Jul 22 Javascript
bootstrap-closable-tab可实现关闭的tab标签页插件
Aug 09 Javascript
js前端图片加载异常兜底方案
Jun 21 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
Extended CHM PHP 语法手册之 DIY
2006/10/09 PHP
使用php实现快钱支付功能(涉及到接口)
2013/07/01 PHP
php中文验证码实现方法
2015/06/18 PHP
PHP  Yii清理缓存的实现方法
2016/11/10 PHP
PHP实现的redis主从数据库状态检测功能示例
2017/07/20 PHP
jQuery easyui datagrid动态查询数据实例讲解
2013/02/26 Javascript
js 操作符汇总
2014/11/08 Javascript
jquery实现标签上移、下移、置顶
2015/04/26 Javascript
javascript事件模型介绍
2016/05/31 Javascript
原生JS控制多个滚动条同步跟随滚动效果
2017/12/22 Javascript
bootstrap table表格插件之服务器端分页实例代码
2018/09/12 Javascript
微信小程序车牌号码模拟键盘输入功能的实现代码
2018/11/11 Javascript
详解vue-router导航守卫
2019/01/19 Javascript
javascript 关于赋值、浅拷贝、深拷贝的个人理解
2019/11/01 Javascript
JavaScript前端实现压缩图片功能
2020/03/06 Javascript
JavaScript实现拖动对话框效果的实现代码
2020/10/12 Javascript
[03:55]DOTA2完美大师赛选手传记——LFY.MONET
2017/11/18 DOTA
python模块之paramiko实例代码
2018/01/31 Python
python3 requests中使用ip代理池随机生成ip的实例
2018/05/07 Python
解决pycharm 远程调试 上传 helpers 卡住的问题
2019/06/27 Python
django项目中使用手机号登录的实例代码
2019/08/15 Python
Python实现图像的垂直投影示例
2020/01/17 Python
tensorflow 重置/清除计算图的实现
2020/01/19 Python
Python爬虫爬取电影票房数据及图表展示操作示例
2020/03/27 Python
Python使用pycharm导入pymysql教程
2020/09/16 Python
英国豪华文具和皮具配件经典老品牌:Smythson(斯迈森)
2018/04/19 全球购物
荟萃全球保健品:维他购
2018/05/09 全球购物
台湾7-ELEVEN线上购物中心:7-11
2021/01/21 全球购物
华为慧通笔试题
2016/04/22 面试题
会计实习生工作总结的自我评价
2013/10/07 职场文书
体育教师自我鉴定
2014/02/12 职场文书
模具专业自荐信
2014/05/29 职场文书
中秋晚会策划方案
2014/06/12 职场文书
庆祝新中国成立65周年“向国旗敬礼”网上签名寄语
2014/09/27 职场文书
党支部培养考察意见
2015/06/02 职场文书
《遗弃》开发商删推文要跑路?官方回应:还在开发
2022/04/03 其他游戏