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 常用关键字列表集合
Dec 04 Javascript
jQuery之网页换肤实现代码
Apr 30 Javascript
详解JavaScript语法对{}处理的坑爹之处
Jun 05 Javascript
javascript实现实时输出当前的时间
Apr 27 Javascript
js密码强度校验
Nov 10 Javascript
xmlplus组件设计系列之树(Tree)(9)
May 02 Javascript
在vue中获取dom元素内容的方法
Jul 10 Javascript
利用Vue2.x开发实现JSON树的方法
Jan 04 Javascript
JS实现二维数组横纵列转置的方法
Apr 17 Javascript
详解vue+webpack+express中间件接口使用
Jul 17 Javascript
详解vue的数据劫持以及操作数组的坑
Apr 18 Javascript
jQuery 筛选器简单操作示例
Oct 02 jQuery
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
php实现通过ftp上传文件
2015/06/19 PHP
PHP中抽象类和抽象方法概念与用法分析
2016/05/24 PHP
PHP curl 或 file_get_contents 获取需要授权页面的方法
2017/05/05 PHP
php设计模式之策略模式实例分析【星际争霸游戏案例】
2020/03/26 PHP
javascript textarea光标定位方法(兼容IE和FF)
2011/03/12 Javascript
js自定义方法通过隐藏iframe实现文件下载
2013/02/21 Javascript
JQuery中上下文选择器实现方法
2015/05/18 Javascript
jQuery判断一个元素是否可见的方法
2015/06/05 Javascript
JavaScript编写简单的计算器
2015/11/25 Javascript
基于JavaScript获取鼠标位置的各种方法
2015/12/16 Javascript
jquery Deferred 快速解决异步回调的问题
2016/04/05 Javascript
jQuery Ajax 异步加载显示等待效果代码分享
2016/08/01 Javascript
Bootstrap基本组件学习笔记之进度条(15)
2016/12/08 Javascript
vue.js element-ui validate中代码不执行问题解决方法
2017/12/18 Javascript
Vue触发式全局组件构建的方法
2018/11/28 Javascript
详解如何使用React Hooks请求数据并渲染
2020/10/18 Javascript
springboot+vue实现文件上传下载
2020/11/17 Vue.js
[54:51]Ti4 冒泡赛第二轮LGD vs C9 3
2014/07/14 DOTA
python正则表达式去掉数字中的逗号(python正则匹配逗号)
2013/12/25 Python
python异步任务队列示例
2014/04/01 Python
Python实现将n个点均匀地分布在球面上的方法
2015/03/12 Python
python 多线程实现检测服务器在线情况
2015/11/25 Python
Python 性能优化技巧总结
2016/11/01 Python
Python中实现变量赋值传递时的引用和拷贝方法
2018/04/29 Python
pygame游戏之旅 调用按钮实现游戏开始功能
2018/11/21 Python
python系统指定文件的查找只输出目录下所有文件及文件夹
2020/01/19 Python
中学老师的自我评价
2013/11/07 职场文书
污水厂厂长岗位职责
2014/01/04 职场文书
生产部岗位职责范文
2014/02/07 职场文书
品德评语大全
2014/05/05 职场文书
工作检讨书范文
2015/01/23 职场文书
财务总监岗位职责范本
2015/04/03 职场文书
2015年民兵整组工作总结
2015/07/24 职场文书
mysql 如何获取两个集合的交集/差集/并集
2021/06/08 MySQL
GO语言字符串处理函数之处理Strings包
2022/04/14 Golang
SQL Server中的游标介绍
2022/05/20 SQL Server