垃圾回收语言通过周期性地检查那些之前被分配出去的内存是否可以从应用的其他部分访问来帮助开发者管理内存。开发者只需要知道一块已分配的内存是否会在将来被使用,而不可访问的内存可以通过算法确定并标记以便返还给操作系统。
引起垃圾收集语言内存泄露的主要原因是不必要的引用。
Mark and Sweep
从根节点开始递归地遍历每一个子节点,根节点通常是那些引用被保留在代码中的全局变量。能够被访问的内存被认为是活动的,其余被认为是垃圾,将其释放给操作系统
JS常见的内存泄漏
- 意外的全局变量
function foo(arg){
bar = "this is a hidden global variable"; //未声明的变量
this.variable = "this points to Windows"; //this指向全局变量
}
解决方法:
use strict
可以检查避免创建意外的全局变量,使用时避免将其使用在全局作用域中(多个文件合并时可能会引起非严格模式下文件报错)在JSLint和JSHint中,’use strict’ 出现在函数体外会给出警告。
//好的写法是使用立即执行函数
(function(){
"use strict"
function a(){
//something
}
function b(){
//something
}
})()
此外,当使用全局变量临时存储和处理大量信息时,需要确保全局变量使用过后置为null或重新赋值。
常见的与全局变量相关的引发内存增长的原因是缓存,最好的做法是,给缓存容量设置上限,避免缓存的无限制增长引起很高的内存消耗。
2. 被遗漏的计时器和回调函数
3. DOM之外的引用, 有些情况下将 DOM 结点存储到数据结构中会十分有用。假设你想要快速地更新一个表格中的几行,如果你把每一行的引用都存储在一个字典或者数组里面会起到很大作用。如果你这么做了,程序中将会保留同一个结点的两个引用:一个引用存在于 DOM 树中,另一个被保留在字典中。如果在未来的某个时刻你决定要将这些行移除,则需要将所有的引用清除。
4. 一些闭包
var theThing = null;
var replaceThing = function(){
var originalThing = theThing;
var unused = function(){
if(originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function(){
console.log(someMessage);
}
};
};
setInterval(replaceThing,1000);
//每次调用 replaceThing 时,theThing 都会得到新的包含一个大数组和新的闭包(someMethod)的对象。同时,没有用到的那个变量持有一个引用了 originalThing(replaceThing 调用之前的 theThing)闭包。someMethod 的闭包作用域和 unused 的作用域是共享的。unused 持有一个 originalThing 的引用。尽管 unused 从来没有被使用过,someMethod 可以在 theThing 之外被访问。而且 someMethod 和 unused 共享了闭包作用域,即便 unused 从来都没有被使用过,它对 originalThing 的引用还是强制它保持活跃状态(阻止它被回收)。当这段代码重复运行时,将可以观察到内存消耗稳定地上涨.