JavsScript在变量创建的时候分配内存,然后在它们不再使用时“自动”释放,就是被称为垃圾回收。“自动”这个词很容易让人混淆,让我们误以为不需要去管理内存。其实这个“自动”也有其处理的逻辑,深入地了解其运行机制,能让我们写出更健壮的JavaScript代码,免去内存泄漏的烦恼。
内存生命周期
不管什么程序语言,内存生命周期基本是一致的:
- 分配你所需要的内存
- 使用分配到的内存(读/写)
- 不需要时将其回收
其中,第一步和第二步并不需要我们关心。我们关注的是:什么情况才会被当成不需要的时候。
垃圾回收
JavaScript具有自动垃圾回收机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。它的原理其实很简单:找到那些不再继续使用的变量,然后释放其占用的内存。
我们都知道局部函数的变量都是在运行的时候分配内存,然后执行结束的时候释放内存。在这种情况下,很容易判断变量是否还有存在的必要。但并非所有情况都这么容易判断的。因此垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来回收其占用的内存。用于标识无用变量的策略通常有两种:
- Reference-counting(引用计数)
- Mark-and-sweep(标记清除)
引用
一个对象如果有访问另一个对象的权限,就叫做一个对象引用另一个对象。
引用计数
其实这是一个很粗略的方式:只要一个对象没有被引用,那就把它当垃圾处理了。这个算法很简单实现,但是有一个致命的问题就是无法解决__循环引用__的问题:
var div;
window.onload = function(){
div = document.getElementById("myDivElement");
div.circularReference = div;
div.lotsOfData = new Array(10000).join("*");
};
这时DOM 元素myDivElement
就永远不会被回收了。
标记清除
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
这个算法假定是有一系列的被称为root的对象,也就是根对象,在JavaScript就是全局对象window
。
然后垃圾回收器就定期地从全局对象开始扫描,寻找所有被全局对象或其他对象引用的对象。
这句话略绕口,简而言之就是,首先寻找到所有被全局对象引用的对象,然后再寻找这些对象所引用的对象,以此循环递归寻找到所有的可被访问对象。
这样,垃圾回收器就可以知道所有的可访问对象和不可访问对象。
本文主要内容参考:Memory Management