什么是内存泄露
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。 有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。
1 | char * buffer; |
可以看一下上面c的代码, malloc
方法用来申请内存,使用完毕之后,必须自己用free
方法释放内存。 这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为”垃圾回收机制”(garbage collector),关于垃圾回收机制可以参考我的上篇博客
内存泄漏的识别方法
怎样可以观察到内存泄漏呢? 经验法则是,如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。(咳咳,回来) 这要我们实时查看内存占用。
浏览器方法
- 打开开发者工具,选择 Timeline 面板
- 在顶部的Capture字段里面勾选 Memory
- 点击左上角的录制按钮。
- 在页面上进行各种操作,模拟用户的使用情况。
- 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。
如果内存占用基本平稳,接近水平,就说明不存在内存泄漏。 反之,就是内存泄漏了。
命令行方法
命令行可以使用 Node
提供的 process.memoryUsage
方法。
1 | console.log(process.memoryUsage()); |
process.memoryUsage
返回一个对象,包含了 Node
进程的内存占用信息。该对象包含四个字段,单位是字节,含义如下。
Resident Set(常驻内存) |
---|
Code Segment(代码区) |
Stack(Local Variables, Pointers) |
Heap(Objects, Closures) |
Used Heap |
rss(resident set size)
:所有内存占用,包括指令区和堆栈。heapTotal
:”堆”占用的内存,包括用到的和没用到的。heapUsed
:用到的堆的部分。external
: V8 引擎内部的 C++ 对象占用的内存。
判断内存泄漏,以heapUsed
字段为准。
WeakMap
前面说过,及时清除引用非常重要。但是,你不可能记得那么多,有时候一疏忽就忘了,所以才有那么多内存泄漏。
最好能有一种方法,在新建引用的时候就声明,哪些引用必须手动清除,哪些引用可以忽略不计,当其他引用消失以后,垃圾回收机制就可以释放内存。这样就能大大减轻程序员的负担,你只要清除主要引用就可以了。
ES6 考虑到了这一点,推出了两种新的数据结构:`WeakSet` 和 `WeakMap`。它们对于值的引用都是不计入垃圾回收机制的,所以名字里面才会有一个"Weak",表示这是弱引用。
下面以 WeakMap
为例,看看它是怎么解决内存泄漏的。
1 | const wm = new WeakMap(); |
上面代码中,先新建一个 Weakmap
实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap
里面。这时,WeakMap
里面对element的引用就是弱引用,不会被计入垃圾回收机制。
也就是说,DOM 节点对象的引用计数是1,而不是2。这时,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap
保存的这个键值对,也会自动消失。
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap
。
- 本文作者: gtt
- 本文链接: https://gtt011029.github.io/posts/230/