Javascript 会找出不再使用的变量,不再使用意味着这个变量生命周期的结束。Javascript 中存在两种变量——全局变量和局部变量,全部变量的声明周期会一直持续,直到页面卸载, 现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引入计数(低级浏览器)。
由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
1、标记清除
当变量进入环境时,将这个变量标记为“进入环境”;当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存
function f () {
// 函数执行时,a b 分别被标记 进入环境
var a = 1
var b= 2
}
f() // 函数执行结束,a b 被标记 离开环境,被回收
2.引入计数(低级浏览器)
引入计数是一种不太常见的垃圾回收策略是引用计数。当变量声明,第一次赋值时记为1,然后当这个变量值改变时,记录为0,将计数为0的回收
function f() {
var objA= new Object();
var objB= new Object();
objA.n= objB;
objB.n= objA;
}
/*在这个例子中,objA和objB通过各自的属性相互引用;也就是说这两个对象的引用次数都是2。
在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,
objA和objB还将会继续存在,因为他们的引用次数永远不会是0。
这样的相互引用如果说很大量的存在就会导致大量的内存泄露。*/
//像上面这种情况就需要手动将变量的内存释放
objA.n = null
objB.n = null
a.意外的全局变量引起的内存泄露
通俗点就是:内存泄漏就是你创建的变量因为一些原因无法被销毁,你自己也访问不到了
原因: 全局变量不会被回收
/*在函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明的是一个全局变量!(如下代码)
只有当页面被关闭后才会被销毁. 所以这种写法就会造成内存泄露*/
function f1() {
a= "全局变量"
}
console.log(a);// 全局变量
/*下面这种情况this被指向了全局变量 window, 意外的创建了全局变量.
如果使用这些全局变量用来暂存大量的数据, 记得在使用后, 对其重新赋值为 null.*/
function f2() {
this.a= "全局变量"
}
console.log(a);// 全局变量
解决:使用严格模式可以避免
b.闭包引起的
原因: 活动对象被引用,使闭包内的变量不会被释放
/*由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,
否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。*/
function test(){
var element = document.getElementById('test');//element 用完之后一直驻留在内存中
var id = element.id;
element.onclick = function () {
console.log(id);//这里导致内存泄露
};
// element = null;// 元素操作完之后主动销毁
}
test();
解决: 将活动对象赋值为null(element = null;)
c.被清理的DOM元素的引用
原因: 虽然DOM被删掉了,但对象中还存在对DOM的引用
var elements = {
dom: $("#MathJax_Message")
}
function f() {
elements.dom.html('MathJax_Message');
}
function removeDom() {
elements.dom.remove();
}
f();
removeDom();
console.log(elements.dom);
// n.fn.init [div#MathJax_Message, context: document, selector: "#MathJax_Message"]
解决: 将对象赋值为null // elements.dom=null
d.被遗忘的定时器或回调
原因: 定时器内部实现闭包,回调也是闭包
function fn() {
return 2
}
var oTxt = fn();
setInterval(function() {
var oHtml = document.getElementById("test")
if(oHtml) {
oHtml.innerHTML = oTxt;
}
}, 1000); // 每 1 秒调用一次
/*如果后续 oHtml 元素被移除, 整个定时器实际上没有任何作用. 但如果你没有回收定时器,
整个定时器依然有效, 不但定时器无法被内存回收, 定时器函数中的依赖也无法回收. 在这个案例中的 fn 也无法被回收.*/
解决: 清理定时器clearInterval、null