Memory management Javascript中的内存管理
看看代码。让我们假设每个语句需要0毫秒才能完成。printAfter2是一个简单的函数,它在调用2秒钟后打印传递给它的字符串Memory management Javascript中的内存管理,memory-management,garbage-collection,v8,Memory Management,Garbage Collection,V8,看看代码。让我们假设每个语句需要0毫秒才能完成。printAfter2是一个简单的函数,它在调用2秒钟后打印传递给它的字符串 printAfter2 = (obj) => { setTimeout(() => { console.log(JSON.stringify(obj)); }, 2000) } 在下面的代码中,我们创建了一个函数 定义时间为0毫秒的块范围变量obj 以obj(类型为Object)作为参数在0毫秒时调用函数。由于传递的参数
printAfter2 = (obj) => {
setTimeout(() => {
console.log(JSON.stringify(obj));
}, 2000)
}
在下面的代码中,我们创建了一个函数
- 定义时间为0毫秒的块范围变量obj
- 以obj(类型为Object)作为参数在0毫秒时调用函数。由于传递的参数是对象,因此其引用将传递给函数
- 然后是console.log函数调用。之后,块在时间0毫秒结束,因此块作用域变量obj也将被销毁
- 在时间2000时,printAfter2函数获取传递给它的参数值。在这种情况下,它是一个变量的引用,到目前为止应该销毁该变量。但这并没有像预期的那样奏效。它在2000毫秒时打印相同的原始obj,应该在0毫秒时销毁。为什么会这样
当变量/参数
obj
超出范围时,这并不意味着任何东西都会立即被销毁。这只意味着对某个对象的一个引用消失,这使得该对象有资格进行垃圾收集,当且仅当该对象是对它的最后一个引用时。垃圾收集器最终(下次运行时)将释放属于不再可访问的对象(即没有对这些对象的引用)的内存。让我们看一个更简单的例子,没有任何闭包:
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)行显然分配了一个对象,并使用变量o1
来引用它。注意,对象和变量之间有区别;特别是它们有不同的寿命。
第(2)行将对象传递给函数;当函数执行时(例如在第(3)行),有两个变量引用同一对象:o1
在外部范围内,以及f1
的范围内obj
。
当f1
在第(4)行终止时,变量obj
超出范围,但仍可通过o1
访问对象。
第(5)行创建一个新变量,再次引用同一对象。这在概念上非常类似于将其传递给某个函数。
当o1
停止引用第(6)行中的对象时,这不会使该对象符合第(7)行中的垃圾收集条件,因为o2
仍在引用它(“保持它活动”)。只有当o2
也被重新分配或超出范围时,对象才会变得不可访问:如果垃圾收集器在执行到达第(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.
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.