JavaScript 的内存管理和垃圾回收,是个略生僻的话题,因为在JavaScript 中不显式执行内存操作,不过最好了解它如何工作。

在低级语言(如 C)中,开发人员需要使用 malloc()、calloc()、realloc()和 free()方法手动分配和释放内存。 在高级语言(如 Java 和 JavaScript)中,不需要明确分配或释放内存。 JavaScript 值在实体(对象、字符串等)创建时分配,不再使用时自动释放。 这个过程称为垃圾回收(Garbage collection,GC)

内存生命周期:

无论何种语言(高级或低级),内存生命周期都如下面所示。

Memory Life cycle

在高级语言中,我们只明确的读写内存(使用内存)。 在低级语言中,开发人员需要明确的执行所有三个步骤。

分配和回收是自动发生的,然而这并不意味着开发人员不需要关心内存管理。 不良编码导致内存泄漏,这种情况下,内存已经不再被应用程序使用,却未被释放。 因此,更多地了解内存管理非常重要。

JavaScript 的内存分配:

声明变量时,JavaScript 自私为变量分配内存。

var numberVar = 100;                // 为数字分配内存
var stringVar = 'node simplified';  // 为字符串分配内存
var objectVar = {a: 1};             // 为对象分配内存
var a = [1, null, 'abra'];          // 为数组分配内存
function f(a) {
  return a + 2;
} // 为函数分配内存

内存不再需要时,分配的内存会被释放。 内存泄漏,以及大多数内存相关问题,都在释放内存时发生。 最难的环节是找出不再使用的内存,并使用垃圾回收器追踪。

垃圾回收:

垃圾回收是找出应用程序不再使用的内存并释放的过程。 为了寻找哪些内存不再使用,垃圾回收器要用到一些算法,在这一部分,我们分析主要的垃圾回收算法和它们的局限性。 我们研究以下算法:

  • 引用计数垃圾回收。
  • 标记-清除算法。

引用计数垃圾回收:

这是最重要的垃圾回收算法。 在引用计数算法中,如果没有对某个对象的引用,它会自动被垃圾回收。 该算法将零引用对象视为应用程序不再使用的对象。

Example:

var o = { a: { b: 2 } };
// 2 个对象被创建。其中一个被另一个作为属性而引用。
// 显然,没有对象可以被垃圾回收。

o = 1;     // 原来作为 o 的 a 属性的对象,已经没有引用。可以被垃圾回收。

缺陷:循环依赖

function func() {
  var o = {};
  var o2 = {};
  o.a = o2; // o 引用 o2
  o2.a = o; // o2 引用 o
  return 'true';
}

func();

考察上面的代码片段,其中 o 被 o2 引用,o2 被 o 引用,形成一个循环。 离开该方法范围时,这两个对象是无用的。 但是,垃圾收集器无法释放内存,因为这两个内存仍然互相引用。 这导致应用程序的内存泄漏。

标记-清除算法:

垃圾回收器使用此算法在对象不可到达时释放内存,而不是零引用对象。

垃圾回收器首先找到所有全局对象或根对象,并找到所有对这些全局对象的引用,以及对引用对象的引用,依此类推。 使用该算法,垃圾回收器识别出可达到和不可达到对象。 所有无法到达的对象都将自动被垃圾回收。

本文首发在 Node Simplified。 看看 Top Javascript Tips and Tricks

如果喜欢本文,点赞并转发给开发者朋友吧 谢谢阅读。