什么是内存泄漏
内存泄漏可以定义为:应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收。
JavaScript 是一种垃圾回收语言。垃圾回收语言通过周期性地检查先前分配的内存是否可达,帮助开发者管理内存。换言之,垃圾回收语言减轻了“内存仍可用”及“内存仍可达”的问题。两者的区别是微妙而重要的:仅有开发者了解哪些内存在将来仍会使用,而不可达内存通过算法确定和标记,适时被操作系统回收。
1.意外的全局变量
我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
function foo(arg){
bar = "this is a hidden global variable";
//window.bar = "this is an explicit global variable";
}
这里foo函数内部,创建了一个全局变量,造成泄露一个字符串。但是在平时的书写中,可能会出现更严重的情况
在 JavaScript 文件头部加上 ‘use strict’,可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。
如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。与全局变量相关的增加内存消耗的一个主因是缓存。
2.被遗忘的计时器或回调函数
我们设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收
var someResourse = getDate();
setInterval(function() {
var node = document.getElementById('Node');
if(node){
node.innerHTML = JSON.stringfy(someResourse);
}
},1000);
与节点或数据关联的计时器不再需要,node 对象可以删除,整个回调函数也不需要了。可是,计时器回调函数仍然没被回收(计时器停止才会被回收)。同时,someResource 如果存储了大量的数据,也是无法被回收的。
3.脱离 DOM 的引用
我们获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。
var elements = {
button: document.getElementById('button')
};
function doStuff() {
button.click();
}
function removeButton() {
// 按钮是 body 的后代元素
document.body.removeChild(document.getElementById('button'));
// 此时,仍旧存在一个全局的 #button 的引用
// elements字典; button 元素仍旧在内存中,不能被垃圾回收器(GC) 回收。
}
同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。
4.闭包
不合理的使用闭包,从而导致某些变量一直被留在内存当中。
闭包的作用域一旦创建,它们有同样的父级作用域,作用域是共享的。本质上,闭包的链表已经创建,每一个闭包作用域携带一个指向大数组的间接的引用,造成严重的内存泄漏。