浅谈Javascript的垃圾回收机制与内存泄漏问题

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中

  1. 打开开发者工具,选择 performance 面板
  2. 勾选 Memory和screenshots
  3. 点击左上角的录制按钮。
  4. 在页面上进行各种操作,模拟用户的使用情况。
  5. 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。

如果内存占用基本平稳,接近水平,就说明不存在内存泄漏。

反之,就是内存泄漏了。