这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
这是内存控制章节的第三篇文章,高效使用内存
- 第一篇:V8 的内存限制和原因
- 第二篇:V8 的垃圾回收机制
前面两篇文章讲到,由于 V8 垃圾回收机制的限制,nodejs 在使用内存时有一些限制,只能使用部分内存(64位系统下约为1.4 GB,32位系统下约为0.7 GB),另外浅析了一个 V8 垃圾回收机制中,对于老生代和新生代的回收算法。
这篇文章讲在 V8 面前,我们开发时需要注意什么,才能让 V8 垃圾回收机制更高效的工作
作用域
触发垃圾回收第一个要讲的是作用域,JavaScript 中可以形成作用域的有:函数调用、with、全局作用域。
比如:
let foo = function() {
let local = {};
}
foo
函数每次调用的时候都会创建对应的作用域,函数执行结束后,作用域将被销毁,同时局部变量 local
随作用域的销毁而销毁。被局部变量引用的对象的生命周期很短,会被分在新生代的 From 空间中,作用域释放后,引用的对象会在下一次垃圾回收时被释放
标识符查找
let bar = function () {
console.log(local);
};
比如这个例子,在执行 bar
函数时,会遇到 local 变量,首先会在当前作用域查找,如果查不到,会依次向上级的作用域查找,直到找到全局作用域,如果都找不到,会抛出一个未定义的错误。
变量的主动释放 如果是全局变量(不通过 var、let、const 声明,或者定义在 global 下的),要到进程退出后才会释放,会导致应用的对象常驻内存(老生代中)。
如果需要释放内存,需要使用 delete 删除引用关系(不推荐,有可能干扰 V8 的优化),或者通过赋值的方式解除引用。
global.foo = "I am global object";
global.foo = undefined; // or null;
闭包
刚刚我们说过,作用域链上的对象访问是向上查找的,外部不能向内部访问,比如下面这个例子就会报错的:
let foo = function () {
(function () {
var local = "局部变量";
}());
console.log(local);
};
让外部作用域访问内部作用域中变量的方法叫做闭包(closure)。这得益于高阶函数的特性:函数可以作为参数或返回值
let foo = function () {
let bar = function () {
let local = '123';
return function () {
return local;
}
}
let baz = bar();
console.log(baz())
}
foo();
正常来说 local 会随着 bar 的作用域销毁被回收,但是因为 bar 返回的是一个函数,函数中可以访问 local,所以外部函数可以通过这个函数访问 local。
闭包的问题是,一旦有变量引用了这个中间函数,这个中间函数不会被释放,中间函数所在的外层作用域也不会被释放,除非不再有引用,才会逐步释放
总结
闭包和全局变量会导致内存无法立即回收,由于 V8 的内存限制,要注意不要让这类变量无限制增加,因为它们会让老生代的对象增多。