Javascript的内存泄漏分析

     作为程序员(更高大尚的称谓:研软件研发)的我们,无论是用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操作,那么恭喜你,内存一定泄漏了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PHP在线

BOM相关知识

history对象 history对象保存着用户上网的历史记录,从窗口被打开那一刻起。属于window对象的属性。 go()方法可以在用户的历史记录中任意跳转...

3589
来自专栏马涛涛的专栏

模块化、闭包与立即执行函数的使用、MVC里的V和C

将js分成不同的几个模块后,然后使用文件引入,但是会出现问题:如果使用var 声明变量,那么就会成为全局变量,这样容易覆盖.

1451
来自专栏GreenLeaves

Jquery filter()方法简介

利用filter函数可以从wrapper set中过滤符合条件的dom元素。 如下图html代码,假如我们要获取类名为filter的<a>标签,用filter方...

1989
来自专栏十月梦想

Promise对象、传参以及错误处理

在大部分情况下我们的程序都是在进行异步操作,需要嵌套多次callback,使得程序变得复杂!ES6中提供了Promise对象,将非阻塞I/O变为阻塞I/O,把异...

4221
来自专栏DT乱“码”

jquery中ajax参数说明

jquery中的ajax方法参数总是记不住,这里记录一下。 1.url:  要求为String类型的参数,(默认为当前页地址)发送请求的地址。 2.type:...

2018
来自专栏对角另一面

读Zepto源码之Callbacks模块

Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promi...

2170
来自专栏jmeter高手高高手

JMeter(十)-正则表达式关联

jmeter中,接口自动化的关键在于参数关联。比如需要登录的接口,如何调用登录口令?一个增删改查的闭环,如何将接口参数上下传递?下面就以实际的例子来仔细说一说

1272
来自专栏FreeBuf

Python黑客学习笔记:从HelloWorld到编写PoC(上)

本系列文章适合CS在读学生和万年工具党,本文会在英文原文的基础上做些修改,并适当增加些解释说明。 ? 本篇包含原文的前几部分: 0x0 – Getting St...

27410
来自专栏北京马哥教育

Linux Bash脚本15分钟进阶教程

这里的技术技巧最初是来自谷歌的“Testing on the Toilet” (TOTT)。这里是一个修订和扩增版本。

700
来自专栏JetpropelledSnake

Python入门之用Python统计代码行

Pycharm每天都要写很多代码,如何统计每天的代码行数呢?作为一个目标十万行的coder,要想想办法! 题目:有个目录,里面是你自己写过的程序,统计一下你写过...

3428

扫码关注云+社区