抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

什么是内存泄露

程序的运行需要内存。只要程序提出要求,操作系统就会给内存。进程应及时释放不再用到的内存。不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
大多数语言提供自动内存管理,减轻程序员的负担,这被称为垃圾回收机制(garbage collector)

垃圾回收

引用计数

思想:跟踪记录所有值被引用的次数
(阮一峰说引用计数是最常使用的,红宝书说到 2008 年为止标记清除是最常用的)
JS 引擎有一张”引用表”,保存了内存里面所有的资源(通常是各种,比如数组)的引用次数。如果一个值的引用次数是 0,就表示这个值不再用到了,因此可以将这块内存释放
如果一个值不再需要了,引用数却不为 0,垃圾回收机制无法释放这块内存,从而导致内存泄漏

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

arr = null;

上面代码中,数组[1, 2, 3, 4]是一个值,会占用内存。变量 arr 是仅有的对这个值的引用,因此引用次数为 1。尽管后面的代码没有用到 arr,它还是会持续占用内存
可以手动解除引用:让变量 = null。则数值这个值就能被垃圾回收机制释放了

缺点

循环引用的情况,eg. 对象 A 包含一个指向对象 B 的指针,而对象 B 中也包含一个指向 A 的指针。采用引用计数策略时,函数执行后,两个对象还将继续存在,因为它们的引用次数永远不会为 0

标记清除

思路:给当前不使用的值加上标记,然后再回收其内存
只有在环境中的变量及被环境中的变量引用的变量(闭包)标记

1
2
3
4
5
function test() {
var a = 10; //被标记"进入环境"
var b = "hello"; //被标记"进入环境"
}
test(); //执行完毕后之后,a和b又被标记"离开环境",被回收

常见 JavaScript 内存泄露

意外的全局变量

eg. 如下代码,这种生命方式等于 this.bar=.....,函数独立调用的话 this 为 window,所以 bar 是全局变量

1
2
3
function foo(arg) {
bar = "this is a hidden global variable";
}

JavaScript 文件头部加上 ‘use strict’,可以避免此类错误发生

但是其实我们自己设全局变量也是有风险的,因为全局变量被认为的不可回收的(标记清除机制无法清除,因为全局环境到最后才会销毁)。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null

被遗忘的 setInterval 或回调函数

用完 setInterval 记得 clearInterval

闭包

不解释

内存泄漏的识别方法

Chrome 浏览器

控制台-profiles

Node

process.memoryUsage(),返回一个对象,包含了 Node 进程的内存占用信息

解决方法

  1. 找到问题后手动释放内存
  2. 使用两种新的数据结构:WeakSet 和 WeakMap。它们对于值的引用都是不计入垃圾回收机制

参考资料

《Javascript 高级程序设计》

阮一峰-JavaScript 内存泄漏教程

评论