JS垃圾回收and内存泄漏

垃圾回收机制:

  1. 找出不再继续使用的变量,然后释放其占用的内存,垃圾回收器会按照固定的时间间隔清除(或者代码中预定的收集时间)周期性的执行这一操作
  2. 垃圾收集器必须跟踪那个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来收回其占用的内存。

通常垃圾回收机制分两种:

1.标记清除:

工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

工作流程:

  1. 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。

  2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。

  3. 再被加上标记的会被视为准备删除的变量。

  4. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

2.引用计数:

工作原理:跟踪记录每个值被引用的次数。

工作流程:

  1. 声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。

  2. 同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1.

  3. 当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1.

  4. 当引用次数变成0时,说明没办法访问这个值了。

  5. 当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。

什么是内存泄漏?

内存泄露是指用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。即所谓内存泄漏。

1.闭包

由于IE的js对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁驻留在内存中的元素。
闭包可以维持函数内部变量驻留内存,使其得不到释放

function(){
    
    
 var oDiv = document.getElementById('oDiv');//oDiv用完之后一直驻留在内存中
    oDiv.onclick = function () {
    
    
        alert('oDiv.innerHTML');//这里用oDiv导致内存泄露
    };
  }
123456

其无意间将变量oDiv泄漏到内存中,最简单的方法就是手动解除引用,或定义在函数外面。

oDiv = null;
1

2.被遗忘的计时器或回调函数

var someResource = getData();
setInterval(function() {
    
    
    var node = document.getElementById('Node');
    if(node) {
    
    
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);
12345678

这样的代码很常见,如果id为Node的元素从DOM中移除,该定时器仍会存在,同时,因为回调函数中包含对someResource的引用,定时器外面的someResource也不会被释放。

3.没有清理的DOM元素引用

有时,保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。

var elements = {
    
    
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
};
function doStuff() {
    
    
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
}
function removeButton() {
    
    
    document.body.removeChild(document.getElementById('button'));
    // 此时,仍旧存在一个全局的 #button 的引用
    // elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
123456789101112131415

虽然我们用removeChild移除了button,但是还在elements对象里保存着#button的引用,换言之,DOM元素还在内存里面。

4意外的全局变量

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

bar没被声明,会变成一个全局变量,在页面关闭之前不会被释放。

另一种意外的全局变量可能由 this 创建:

function foo() {
    
    
    this.variable = "potential accidental global";
}
// foo 调用自己,this 指向了全局对象(window)
foo();
12345

在 JavaScript 文件头部加上 ‘use strict’,可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。

内存泄漏的识别方法

新版本的chrome在 performance 中查看:

步骤:

  • 打开开发者工具 Performance
  • 勾选 Screenshots 和 memory
  • 左上角小圆点开始录制(record)
  • 停止录制

图中 Heap 对应的部分就可以看到内存在周期性的回落也可以看到垃圾回收的周期,如果垃圾回收之后的最低值(我们称为min),min在不断上涨,那么肯定是有较为严重的内存泄漏问题。

避免内存泄漏的一些方式:

  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收
  • 注意程序逻辑,避免“死循环”之类的
  • 避免创建过多的对象

总而言之需要遵循一条原则:不用了的东西要及时归还

猜你喜欢

转载自blog.csdn.net/weixin_46071217/article/details/108567885
今日推荐