Javascript 中的内存管理

Memory Management in Javascript

看看代码。让我们假设每条语句需要 0 毫秒才能完成。 printAfter2 是一个简单的函数,它在调用 2 秒后打印传递给它的字符串。

printAfter2 = (obj) => {
    setTimeout(() => {
        console.log(JSON.stringify(obj));
    }, 2000)
} 

在下面的代码中,我们创建了一个函数

我们实际上不需要异步函数但忽略它。

(async () => {
    let obj = {name: 'Ali'}
    printAfter2(obj);
    console.log("obj var will be destroyed after this block");
})()

当 variable/parameter obj 超出范围时,并不意味着任何东西都会立即被销毁。这仅意味着对某个对象的引用消失,这使得该对象 符合垃圾收集条件 当且仅当这是对它的最后一次引用时。垃圾收集器最终(下一次 运行s)释放属于不再可达的对象的内存,即没有对它们的引用。让我们看一个更简单的情况,没有任何闭包:

let o1;
function f1(obj) {
  console.log(obj);  // (3)
}                    // (4)
o1 = new Object();   // (1)
f1(o1);              // (2)
let o2 = o1;         // (5)
o1 = null;           // (6)
// (7)
o2 = new Array();
// (8)

第(1)行显然分配了一个Object,并使用变量o1来引用它。请注意,对象和变量之间存在区别;特别是他们有不同的生命周期。 第(2)行将对象传递给函数;当函数执行时(例如在第 (3) 行),有两个变量引用同一个对象:o1 在外部作用域中,objf1 的作用域中。 当 f1 在第 (4) 行终止时,变量 obj 超出范围,但对象仍然可以通过 o1 访问。 第 (5) 行创建了一个新变量,再次引用同一个对象。这在概念上与将其传递给某个函数非常相似。 当 o1 停止引用第 (6) 行中的对象时,这不会使对象符合第 (7) 行中的垃圾收集条件,因为 o2 仍在引用它(“保持活动状态” ”)。只有当 o2 也被重新分配或超出范围时,对象才会变得不可访问:如果垃圾收集器 运行s 在执行到第 (8) 行后的任何时间,对象的内存将被释放.

(旁注:垃圾收集器实际上并不“收集垃圾”或“销毁对象”,因为它根本不接触该内存。它只记录存储对象的内存这一事实现在可以免费用于新的分配。)

在您的示例中,您正在创建一个包含对象引用的闭包 () => console.log(JSON.stringify(obj))。当这个闭包等待它的执行时间时,这个引用将使对象保持活动状态。它只能在闭包完成 运行 并且自身变得不可访问后才能被释放。

换一种方式来说明:

function MakeClosure() {
  let obj = {message: "Hello world"};
  return function() { console.log(JSON.stringify(obj)); };
}

let callback = MakeClosure();
// While the local variable `obj` is inaccessible now, `callback` internally
// has a reference to the object created as `{message: ...}`.
setTimeout(callback, 2000);
// Same situation as above at this point.
callback = null;
// Now the variable `callback` can't be used any more to refer to the closure,
// but the `setTimeout` call added the closure to some internal list, so it's
// not unreachable yet.
// Only once the callback has run and is dropped from the engine-internal list
// of waiting setTimeout-scheduled callbacks, can the `{message: ...}` object get
// cleaned up -- again, this doesn't happen immediately, only whenever the garbage 
// collector decides to run.