内存种类
JavaScript的内存分为三种
- 栈:保存基本数据类型、引用类型的指针。
- 池:池保存常量,一般归类到栈中。
- 堆:保存引用数据类型。
栈的优势就是存取速度比堆要快,但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,垃圾收集器会自动地收走这些不再使用的数据,但是缺点是由于在运行时动态分配内存,所以存取速度较慢。
内存回收
JavaScript 中的内存管理是自动执行的,我们创建的每个变量、方法都会占用一部分内存。当内存不再被使用时,会被垃圾收集器(GC)自动回收。
内存泄露
当不需要的内存因为某种原因没有系统回收。
常见的会造成内存泄露的几个原因:
错误的全局变量
全局作用域内的变量不会被系统自动回收,因为顶级对象一直存在。浏览器环境内顶级对象是window,node环境下是Global。闭包
下面这段代码中c\b\c其实并没有被用到,但是他们存在于add和subtract的作用域链中,占用的无法被系统回收。
华勇
闭包会引用包含函数的整个活动对象。即使闭包不直接引用变量,包含函数的活动对象中也仍然会保存一个引用。(《JavaScript高级程序设计》第三版第184页)
const demo = (function () {
let a = 0;
let b = 0;
let c = 0;
let num = 0;
return {
add() {
return ++num;
},
subtract() {
return --num;
}
}
})();
console
控制台里打印的对象不会被系统回收,建议转成字符串再打印。未停止的定时器
通过setTimeout, setInterval来实现延迟执行,如果没有通过clearTimeout, clearInterval手动停止会一直占用内存,而且在部分浏览器里会即便出现关闭页面也会一直占用内存的情况。移除DOM但未在JS中断开引用
如下面这段代码,打印a时虽然元素已经在页面上被移除,但还是可以输出DOM对象。
var a = document.querySelector('#app');
a.parentNode.removeChild(a);
console.log(a);
``
6. 循环引用
如下,a和b的引用数至少是1,所以不会被GC清理。
```javascript
const a = {};
const b = {};
a.a = b;
b.b = a;
如何减少内存泄漏的风险
- 不再使用的变量及时解除引用a = null;
- 开启严格模式,避免错误的添加全局变量。
- 生产环境减少使用console
- 及时清除定时器