JavaScript内存管理详解
JavaScript会在创建变量(对象,字符串等)时分配内存,并且在不再使用它们时“自动”释放内存,这个自动释放内存的过程称为垃圾回收。
1. 内存的生命周期
1.1. JS 的内存分配
栈内存:存放基本类型;
堆内存:存放引用类型(在栈内存中存一个基本类型值保存对象在堆内存中的地址,用于引用这个对象。)
var count = 3;
var str = "hgtfsad";
var obj = {
key: 1,
value: 'string'
};
var date = new Date();
var ele = document.createElement('div');
1.2. JS 的内存使用
读写内存,也就是使用变量、函数等
var a = 10;
console.log(a);
1.3. JS 的内存回收
JS 有自动垃圾回收机制,原理就是找出那些不再继续使用的值,然后释放其占用的内存。
局部变量:只在函数的执行过程中存在;
全局变量:生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收
var index=0;
var name="ming";
function sum() {
var a=1;
var b=2;
return a+b;
}
console.log(sum());
2. 垃圾回收
2.1 引用
垃圾回收算法主要依赖于引用的概念。
在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。
var name='Bob';
var obj={
key:1,
name:'Alice'
};
name=obj.name; //隐式引用
var info=obj; //显示引用
2.2 引用计数垃圾收集
是最初级的垃圾回收算法,就是看一个对象是否有指向它的引用,若没有其他对象指向它了,说明该对象已经不再需要了。
var people = '';
var obj = {
key:1,
name: 'Alice'
} // 引用计数为1
people = obj.name;//引用计数为2
obj = null;//引用计数为1
people = null;//计数为0
致命的问题:循环引用,相互引用依然存在,计数不为0
function f(){
var o1 = {};
var o2 = {};
o1.a = o2;
o2.a = o1;
return "azerty";
}
f();
2.3. 标记清除算法
从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
3. 内存泄漏
3.1 概念
内存泄漏就是由于疏忽或错误,造成程序未能释放那些已经不再使用的内存,造成内存的浪费。不断出现内存泄漏,则会造成OOM, 进程崩溃。
3.2内存泄漏识别方法
两种方式来判定当前是否有内存泄漏:
多次快照后,比较每次快照中内存的占用情况,如果呈上升趋势,那么可以认为存在内存泄漏
某次快照后,看当前内存占用的趋势图,如果走势不平稳,呈上升趋势,那么可以认为存在内存泄漏
在服务器环境中使用 Node 提供的 process.memoryUsage 方法查看内存情况
3.2 常见内存泄漏案例
案例一:不小心创建全局变量
function foo() {
bar1 = 'some text';
this.bar2 = 'some text';
}
foo();
案例二:未及时关闭定时器
var serverData = loadData();
setInterval(function() {
var renderer = document.getElementById('renderer');
if(renderer) {
renderer.innerHTML = JSON.stringify(serverData);
}
}, 5000);
案例三:闭包
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing) // 对于 'originalThing'的引用
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log("message");
}
};
};
setInterval(replaceThing, 1000);
3.3 如何避免内存泄漏
记住一个原则:不用的东西,及时归还。
减少不必要的全局变量,使用严格模式避免意外创建全局变量。
在使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。
组织好逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。