内存管理
谈到内存管理,就要说一下C语言的内存管理接口,比如malloc()
和free()
。对应的接口意思: 分配 和 释放。而JavaScript
是在创建变量时自动进行了分配内存,并且在不使用它们时自动
释放。释放的过程称为垃圾回收
。
内存生命周期
不管什么语言,内存生命周期基本是一致的:
- 分配需要的内存
- 使用分配到的内存进行读和写
- 不需要该内存时将其释放
在JavaScript中,对应的内存生命周期的变化:
- 创建变量/函数/对象 (对应
内存生命周期
的第1步,分配内存) - 使用值的过程(对应
内存生命周期
的第2步,进行内存的读和写) - 当不再使用或引用值时,垃圾回收器自动释放内存(对应
内存生命周期
的第3步,不需要改内存时将其释放)
JavaScript的垃圾回收
JavaScript的垃圾回收算法主要是依赖于引用的概念
。它有两种垃圾回收算法。
以下垃圾回收算法中,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。
引用计数垃圾回收
这是最初级的垃圾收集算法。当你创建的对象,当前没有其他变量引用指向该对象,即零引用时
,对象将被垃圾回收机制回收。
限制: 循环引用
。无法处理循环引用的事例。在下面的例子中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。
function func() {
var o1 = {};
var o2 = {};
o1.a = o2; // o1 引用 o2
o2.a = o1; // o2 引用 o1
return "xxx"
}
func();
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
从一个根(root)对象(在JavaScript里,根是全局对象)开始,找所有从根开始引用的对象。从根开始,垃圾回收器将找到所有可以获得的对象
和收集所有不能获得的对象
。
当从全局对象出发,无法获得的对象将被垃圾回收器回收。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。
限制: 那些无法从根对象查询到的对象都将被清除。
尽管这是一个限制,但实践中我们很少会碰到类似的情况,所以开发者不太会去关心垃圾回收机制。