垃圾收集器会定期找出那些不再继续使用的变量,然后释放其内存

对象
浏览器的垃圾回收机制针对于 局部变量 ,因为 全局变量 的生命周期结束是直到浏览器页面被卸载才会结束。
局部变量 只在函数的执行过程中存在,在这个过程中会为局部变量在堆栈上分配相应的空间,以存储他们的值,然后在函数中使用这些变量,直至函数执行结束
——闭包由于内部函数的原因,外部函数并不能算是结束
标记无用变量的方式
标记清除
从根部(在 JS 中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收
js 中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为 " 进入环境 "。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为 " 离开环境 "。
function test(){
var a = 10 ; //被标记 ,进入环境
var b = 20 ; //被标记 ,进入环境
}
test(); //执行完毕 之后 a、b又被标离开环境,被回收。垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。 到目前为止,IE9+、Firefox、Opera、Chrome、Safari 的 js 实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。
function test(){
var a = {} ; //a的引用次数为0
var b = a ; //a的引用次数加1,为1
var c =a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
}Netscape Navigator3 是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用。循环引用指的是对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。
function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();以上代码 a 和 b 的引用次数都是 2,fn() 执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为 a 和 b 的引用次数不为 0,所以不会被垃圾回收器回收内存,如果 fn 函数被大量调用,就会造成内存泄露。在 IE7 与 IE8 上,内存直线上升。
我们知道,IE 中有一部分对象并不是原生 js 对象。例如,其内存泄露 DOM 和 BOM 中的对象就是使用 C++ 以 COM 对象的形式实现的,而 COM 对象的垃圾回收机制采用的就是引用计数策略。因此,即使 IE 的 js 引擎采用标记清除策略来实现,但 js 访问的 COM 对象依然是基于引用计数策略的。换句话说,只要在 IE 中涉及 COM 对象,就会存在循环引用的问题。
var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;这个例子在一个 DOM 元素(element) 与一个原生 js 对象(myObject) 之间创建了循环引用。其中,变量 myObject 有一个属性 e 指向 element 对象;而变量 element 也有一个属性 o 回指 myObject。由于存在这个循环引用,即使例子中的 DOM 从页面中移除,它也永远不会被回收。
举个栗子:
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
};这段代码看起来没什么问题,但是 obj 引用了 document.getElementById('element'),而 document.getElementById('element') 的 onclick 方法会引用外部环境中的变量,自然也包括 obj,是不是很隐蔽啊。(在比较新的浏览器中在移除 Node 的时候已经会移除其上的 event 了,但是在老的浏览器,特别是 ie 上会有这个 bug)
回收机制
v8 采用 分代式垃圾回收机制,将内存分为 新生代 和 老生代 ,对新老生代采取不同的回收策略
新生代空间
其中的对象一般存活时间比较短,采用 scavenge算法 (清除、扫气
将内存空间分为 from空间 和 to空间 ,新分配的对象会被放入到 from 空间中,当 from 空间被占满时,新生代 GC 算法就启动了
算法会检查 from 空间 中存活的对象并将其复制到 to 空间 中,如果有失活的对象则会销毁,当复制完成后将 from 空间 与 to 空间 互换,,gc 结束
晋升到老空间
- 经历了一次 scavenge 算法(新生代算法)
- 在 to 空间 的内存占比超过 25%
老生代空间
老生代对象一般存活时间长且数量也多
采用 标记清除算法 (深度优先搜索)和 标记压缩算法
当发生以下情况启动 标记清除算法:
● 某一个空间没有分块的时候
● 空间中对象超过一定限制
● 不能保证新生代中的对象移动到老生代中
标记清除算法会先遍历堆中的所有对象,然后标记活的对象,在标记完成后,销毁所有没有被标记的对象
销毁完成后会造成堆空间出现碎片的情况,当碎片超过一定限制后启动压缩算法:
——在压缩过程中,将存活的对象向堆中一端移动,直到所有对象移动完成,然后清理掉不需要的内存