高效使用内存

134 阅读3分钟
原文链接: mp.weixin.qq.com

高效使用内存是表示让垃圾回收机制更高效的工作。

当我们在代码中声明变量并赋值时,所使用对象的内存就分配在堆中。如果已申请的堆空闲内存不够分配新的对象,将继续申请堆内存,直到堆的大小超过V8的限制为止。

内存分配之后是要回收的,如果不回收就一直占用内存,超出了内存的最大值,就会内存泄漏。所有我们要理解垃圾回收。什么情况会触发垃圾回收呢?

作用域

在JavaScript中能形成作用域的有函数调用、 with 以及全局作用域。

const foo = function () {

    const local = {};

};

foo() 函数在每次被调用时会创建对应的作用域,函数执行结束后,该作用域将会销毁。同时作用域中声明的局部变量分配在该作用域上,随作用域的销毁而销毁。只被局部变量引用的对象存活周期较短。在这个示例中,由于对象非常小,将会分配在新生代中的From空间中。在作用域释放后,局部变量 local 失效,其引用的对象将会在下次垃圾回收时被释放。

主动释放

如果变量是全局变量(定义在 global 变量上),由于全局作用域需要直到进程退出才能释放,此时将导致引用的对象常驻内存(常驻在老生代中)。如果需要释放常驻内存的对象,可以通过 delete 操作来删除引用关系。或者将变量重新赋值,让旧的对象脱离引用关系。在接下来的老生代内存清除和整理的过程中,会被回收释放。下面为示例代码:

global.foo = "I am global object";

console.log(global.foo); // => "I am global object"

delete global.foo;

// 或者重新赋值

global.foo = undefined; // or null

console.log(global.foo); // => undefined

虽然 delete 操作和重新赋值具有相同的效果,但是在V8中通过 delete 删除对象的属性有可能干扰V8的优化,所以通过赋值方式解除引用更好。

闭包

在JavaScript中,实现外部作用域访问内部作用域中变量的方法叫做闭包。这是使用了高阶函数的特性:函数可以作为参数或者返回值。

const foo = function () {

    const bar = function () {

        const local = "局部变量";

        return function () {

            return local;

        };

    };

    const baz = bar();

    console.log(baz());

};

一般而言,在 bar() 函数执行完成后,局部变量local 将会随着作用域的销毁而被回收。但是注意这里的特点在于返回值是一个匿名函数,且这个函数中具备了访问 local 的条件。虽然在后续的执行中,在外部作用域中还是无法直接访问 local ,但是若要访问它,只要通过这个中间函数稍作周转即可。

bar的执行结果是个中间函数赋值给了baz变量,一旦有变量引用这个中间函数,这个中间函数将不会释放,同时也会使原始的作用域不会得到释放,作用域中产生的内存占用也不会得到释放。除非不再有引用,才会逐步释放,foo执行完毕才销毁。

推荐图书