Skip to content
On this page

JavaScript 内存管理

前言

JavsScript在变量创建的时候分配内存,然后在它们不再使用时“自动”释放,就是被称为垃圾回收。“自动”这个词很容易让人混淆,让我们误以为不需要去管理内存。其实这个“自动”也有其处理的逻辑,深入地了解其运行机制,能让我们写出更健壮的JavaScript代码,免去内存泄漏的烦恼。

内存生命周期

不管什么程序语言,内存生命周期基本是一致的:

  1. 分配你所需要的内存
  2. 使用分配到的内存(读/写)
  3. 不需要时将其回收

其中,第一步和第二步并不需要我们关心。我们关注的是:什么情况才会被当成不需要的时候。

垃圾回收

JavaScript具有自动垃圾回收机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。它的原理其实很简单:找到那些不再继续使用的变量,然后释放其占用的内存。

我们都知道局部函数的变量都是在运行的时候分配内存,然后执行结束的时候释放内存。在这种情况下,很容易判断变量是否还有存在的必要。但并非所有情况都这么容易判断的。因此垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来回收其占用的内存。用于标识无用变量的策略通常有两种:

- Reference-counting(引用计数) - Mark-and-sweep(标记清除)

引用

一个对象如果有访问另一个对象的权限,就叫做一个对象引用另一个对象。

引用计数

其实这是一个很粗略的方式:只要一个对象没有被引用,那就把它当垃圾处理了。这个算法很简单实现,但是有一个致命的问题就是无法解决循环引用的问题:

js
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