javascript的垃圾回收机制
Javascript浏览器具有自动垃圾回收机制
最常用的方式是标记清除(mark-and-sweep)
当申明一个变量时,就将这个变量标记为“进入环境”。从逻辑上来讲,永远都不能释放进入环境的变量。当变量离开环境时,标记为“离开环境”
js定期进行垃圾回收(非实时),销毁带“离开环境”标记的变量,回收内存
function test(){
var a = 10 ; // 被标记 ,进入环境
var b = 20 ; // 被标记 ,进入环境
}
test(); // 执行完毕 之后 a、b又被标离开环境,被回收
内存泄漏
内存泄漏是指一块被分配的内存即不能使用也不能回收
浏览器会使用自动垃圾回收机制,但是也会产生一些内存泄漏问题
1. 意外的全局变量
function foo(arg) {
bar = "this is a hidden global variable";
}
bar没被申明,会变成一个全局变量,在页面关闭之前不会被释放
2. 被遗忘的计时器或callback函数
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// 处理 node 和 someResource
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
这里如果node元素从dom中移除,该定时器还是会存在;同时因为回掉函数中用了someResource,定时器外面的someResource也不会被释放
3. 闭包
function bindEvent(){
var obj=document.createElement('xxx')
obj.onclick=function(){
// Even if it is a empty function
}
}
闭包可以维持函数内的局部变量,使得闭包内返回的函数可以调用返回外的局部变量,使其得不到释放,所以会形成内存泄漏
// 将事件处理函数定义在外面
function bindEvent() {
var obj = document.createElement('xxx')
obj.onclick = onclickHandler
}
// 或者在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent() {
var obj = document.createElement('xxx')
obj.onclick = function() {
// Even if it is a empty function
}
obj = null
}
解决方法:将事件处理函数定义在外部,或者在定义事件处理函数的外部函数中,删除对dom的引用。
4. 没有清理的dom元素引用
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image'),
text: document.getElementById('text')
};
function removeButton() {
document.body.removeChild(document.getElementById('button'));
// 此时,仍旧存在一个全局的 #button 的引用
// elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
虽然我们用removeChild移除了button,但是还在elements对象里保存着#button的引用,换言之,DOM元素还在内存里面。
避免内存泄漏的方法
- 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收
- 注意程序逻辑,避免“死循环”之类的
- 避免创建过多的对象
总而言之需要遵循一条原则:不用了的东西要及时归还
内存泄漏查看方法
chrome中
- 打开开发者工具,选择 performance 面板
- 勾选 Memory和screenshots
- 点击左上角的录制按钮。
- 在页面上进行各种操作,模拟用户的使用情况。
- 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。
如果内存占用基本平稳,接近水平,就说明不存在内存泄漏。
反之,就是内存泄漏了。