JavaScript 中的内存管理是自动执行的,而且是不可见的。我们创建基本类型、对象、函数……所有这些都需要内存。一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。当不再需要某样东西时会发生什么? JavaScript 引擎是如何发现并清理它?这就涉及到了JavaScript垃圾回收机制。
浏览器的JavaScript引擎使用的垃圾回收算法是引用计数法,对于循环引用将会导致GC无法回收“应该被回收”的内存。造成了无意义的内存占用,也就是内存泄漏。内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
每隔一段时间, JS 的垃圾收集器都会对变量进行“巡逻”,就和保安巡逻园区一样,让不相干的人赶紧走。当一个变量不被需要了以后,它就会把这个变量所占用的内存空间所释放,这个过程就叫做垃圾回收。
JavaScript的垃圾回收算法分为两种,引用计数法和标记清除法。
引用计数法是最初级的垃圾回收算法,已经被现代浏览器所淘汰了。在学习引用计数法之前,需要首先对引用有一定的概念,你可以认为它就是对当前变量所指向的那块内存地址的描述,先来看一行代码:
var obj={name:'jack'};
当我们在给 obj 赋值的同时,其实就创建了一个指向该变量的引用,引用计数为1,在引用计数法的机制下,内存中的每一个值都会对应一个引用计数
而当我们给 obj 赋值为 null时,这个变量就变成了一块没用的内存,那么此时, obj 的引用计数将会变成 0,它将会被垃圾收集器所回收,也就是 obj 所占用的内存空间将会被释放
我们知道,函数作用域的生命周期是很短暂的,在函数执行完毕之后,里面的变量基本是没用的变量了,不清除的后果就是容易引发内存泄漏,先来看一段代码以及运行结果:
function changeName(){
var obj1={};
var obj2={};
obj1.target=obj2;
obj2.target=obj1;
obj1.age=15;
console.log(obj1.target);
console.log(obj2.target);
}
changeName();
我们可以看到, obj1.target 和 obj2.target 存在互相引用的情况,因为在改变 obj1.age 的同时,obj1.target 和 obj2.target 也同时都被影响到了,它们所指向的引用计数是一致的。
在函数执行完毕的时候, obj1 和 obj2 还是活的好好地,因为 obj1.target 和 obj2.target 的引用计数在执行完毕之后,仍然是 1 ,明明函数执行完毕,但是这种垃圾依然存在,这种函数定义多了,内存泄漏也会是无法避免的。
上面的引用计数法的弊端已经很明显了,那么,现在所要说的标记清除法就不存在这样子的问题。因为它采用的判断标准是看这个对象是否可抵达,它主要分为两个阶段,标记阶段和清除阶段:
垃圾收集器会从根对象(Window对象)出发,扫描所有可以触及的对象,这就是所谓的可抵达
在扫描的同时,根对象无法触及(不可抵达)的对象,就是被认为不被需要的对象,就会被当成垃圾清除
现在再来看下上面的代码
function changeName(){
var obj1={};
var obj2={};
obj1.target=obj2;
obj2.target=obj1;
obj1.age=15;
console.log(obj1.target);
console.log(obj2.target);
}
changeName();
在函数执行完毕之后,函数的声明周期结束,那么现在,从Window对象出发, obj1 和 obj2 都会被垃圾收集器标记为不可抵达,这样子的情况下,互相引用的情况也会迎刃而解。
高级编程语言里面一般都会带垃圾回收机制,比如Java,JavaScript,Python,在没有GC的世界里,需要程序员手动管理内存,比如C语言我们常见的malloc/free,其实就是memory allocation的缩写。想了解其他编程语言的垃圾回收机制可以参考动力节点在线的JavaScript视频教程。
代码小兵49806-11 15:28
代码小兵49806-11 15:51
代码小兵49806-11 16:22
代码小兵51603-29 17:28
暴风城-小飞04-06 20:49