js内存泄漏演示

什么是内存泄漏?

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

垃圾回收机制

最常使用的方法叫做"引用计数"(reference counting):语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

    const arr = [1, 2, 3, 4];
    console.log('hello world');

上面代码中,数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。

如果增加一行代码,解除arr对[1, 2, 3, 4]引用,这块内存就可以被垃圾回收机制释放了。

     let arr = [1, 2, 3, 4];
     console.log('hello world');
     arr = null;

arr重置为null,就解除了对[1, 2, 3, 4]的引用,引用次数变成了0,内存就可以释放出来了。

内存泄漏的识别方法(命令行)

命令行可以使用 Node 提供的process.memoryUsage方法。

console.log(process.memoryUsage());
// { rss: 2770940,
//  heapTotal: 568248,
//  heapUsed: 349392,
//  external: 8771 }

process.memoryUsage 返回一个对象,包含了 Node 进程的内存占用信息。该对象包含四个字段,单位是字节,含义如下。
rss(resident set size):所有内存占用,包括指令区和堆栈。
heapTotal:"堆"占用的内存,包括用到的和没用到的。
heapUsed:用到的堆的部分。
external: V8 引擎内部的 C++ 对象占用的内存。
判断内存泄漏,以heapUsed字段为准。

WeakMap

在新建引用的时候就声明,哪些引用必须手动清除,哪些引用可以忽略不计,当其他引用消失以后,垃圾回收机制就可以释放内存。
ES6 考虑到了这一点,推出了两种新的数据结构:WeakSet 和 WeakMap。它们对于值的引用都是不计入垃圾回收机制的

WeakMap 示例

引用所指向的值占用特别多的内存,就可以通过process.memoryUsage方法看出来。
node --expose-gc //参数表示允许手动执行垃圾回收机制
global.gc(); //动执行一次垃圾回收,保证获取的内存使用状态准确

初始化,手动执行一次垃圾回收, 查看内存占用的初始状态

618DC8~1.PNG

创建对象,手动执行一次垃圾回收, 查看内存占用的初始状态

3C7F4B~1.PNG

在 WeakMap 中添加一个键值对,名为对象 b,键值为一个 510241024 的数组

581068~1.PNG

解除对象 b 的引用 ,再次执行垃圾回收,说明 WeakMap 中的那个长度为 5x1024x1024 的数组被销毁了
F781A8~1.PNG

推荐阅读更多精彩内容