小龙女先生 阅读(20) 评论(0)

     作为程序员(更高大尚的称谓:研软件研发)的我们,无论是用Javascript,还是.net, java语言,肯定都遇到过内存泄漏的问题。只不过他们都有GC机制来帮助程序员完成内存回收的事情,如果你是C++开发者(你懂的)。。。。。,如果你是前端开发者,肯定在使用Javascript(你或者会说,Js是世界上最棒的语言),但我这里也得告诉你,Js的内存泄漏会来得更为突然,或者让你都无法察觉。本文就带大家领略一下Js的风骚:

一、模块化引起的内存泄漏

代码如下:

// module date.js
let date = null;
export default {
    init () {
        date = new Date();
    }
}

// main.js
import date from 'date.js';
date.init();

      上述是我们在现代前端工程方案中常见的代码格式,写一个模块,然后导出这一个模块。这里你应该知道date.js中的date是静态的(也就是你在N处导入date.js这个模块),但他们的date这个变量是共享的,一处改变,其他地方也对应发生变化。

 

二、假OOP范式引起的内存泄漏

在这里我为什么叫他假OOP呢,原因是这代码是想实现OOP范式却让自己掉到坑里去了,先上代码:

var fun = function(arg){
    this.sarg = arg; 
    var self = this;
    return function(){
        console.log(self.sarg);
    }
}
var fn = new fun('data arg');
fn();

首先,定义fun这个函数变量,然后返回一个function(这可以说就是典型的闭包)。闭包函数内引用外面的this对象(var self = this)。

然后,通过new的方式调用fun,返回值用fn接受,这里谁都知道返回的是一个函数,所以可以括号运算符进行执行。

2.1 利用chrome的memory面板进行分析

      定位到memory面板,然后刷新页面,再单击下图中所示的 'collect garbage'图标(也就是像回收站的图标),强制进行一次gc的回收,这样可以确保我们分析的对象就是可以存在内存泄漏的对象(至少他们是gc不可回收的对象)。

此图是上述代码片段在chrome浏览器中执行完成后,不能被gc回收的内存变量。

2.2 我认为的原因

先贴出发生内存泄漏的代码

var fun = function(arg){
    this.sarg = arg; //内存泄漏
    var self = this;//内存泄漏
    return function(){
        console.log(self.sarg);//内存泄漏
    }
}
var fn = new fun('data arg'); //内存泄漏
fn();

我认为的原因有以下几点:

1. 使用new运算符,他会创建一个对象,然后执行构造函数,并将构造函数对应的prototype(也就是原型)复制到新的对象上。

2. 上述new出来的新对象,在执行构造函数时,其this就指向了这个new出来的新对象

3. 然后上述代码在构造函数中又返回了一个函数,且函数中引用了new出来的新对象,返回函数赋值给了fn变量

4. 最的执行fn变量,正确输出我们想要的内容,这样程序就跑了(可以,我们new出来的新对象,没有人管也了,所以他就泄漏了)。

2.3 总结:

     因为正常情况下,我们对一个function进行new操作的时候,在构造函数内是不会进行返回的,其实这个时候new操作默认给你返回的就是构造函数中的this对象。上述代码不建议出现在项目代码中,这是典型的错误写法,并示例只是为了演示泄漏。

 

三、DOM事件引起的内存泄漏

如果你是Jquery的忠粉,这部分可能对你有帮助,先上代码:

//html:
<input type="file" id="file" />
<button type="button" onclick="remove()" >but</button>

//js:
var file = document.getElementById("file");
file.addEventListener('change',function(event){
    console.log(event.target.value);
});
function remove(){
    file.remove();
}

首先我们在html中写两个标签,一个是file、一个是button;然后在js中对file标签绑定了change事件,然后对button绑定一个remove方法,用于移除file标签。

3.1 内存泄漏分析

在我们执行了remove方法后,然后收集内存分析:

     我们还按照示例二相同的操作,打开memory面板,然后执行一次GC回收后收集内存数据,然后查看Detached Dom tree(这就表示与DOM树失去联系的对象),然后我们把鼠标移动到native上,就会显示内存泄漏的代码位置。

Jquery忠粉们可以注意了,无论你是用的bind还是on进行事件的绑定,如果你在移除这些DOM元素前,没有进行相应的unbind或是off操作,那么恭喜你,内存一定泄漏了。