JavaScript 的内存管理和垃圾回收,是个略生僻的话题,因为在JavaScript 中不显式执行内存操作,不过最好了解它如何工作。
在低级语言(如 C)中,开发人员需要使用 malloc()、calloc()、realloc()和 free()方法手动分配和释放内存。 在高级语言(如 Java 和 JavaScript)中,不需要明确分配或释放内存。 JavaScript 值在实体(对象、字符串等)创建时分配,不再使用时自动释放。 这个过程称为垃圾回收(Garbage collection,GC)。
内存生命周期:
无论何种语言(高级或低级),内存生命周期都如下面所示。
在高级语言中,我们只明确的读写内存(使用内存)。 在低级语言中,开发人员需要明确的执行所有三个步骤。
分配和回收是自动发生的,然而这并不意味着开发人员不需要关心内存管理。 不良编码导致内存泄漏,这种情况下,内存已经不再被应用程序使用,却未被释放。 因此,更多地了解内存管理非常重要。
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。
如果喜欢本文,点赞并转发给开发者朋友吧 谢谢阅读。