资讯专栏INFORMATION COLUMN

【拒绝拖延】常见的JavaScript内存泄露原因及解决方案

tangr206 / 1657人阅读

摘要:前言内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。这里就讲一些常见会带来内存泄露的原因。解决方案和都设为。

前言

内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。这里就讲一些常见会带来内存泄露的原因。

0. 全局变量
JavaScript自由的其中一种方式是它可以处理没有声明的变量:一个未声明的变量的引用在全局对象中创建了一个新变量。在浏览器的环境中,全局对象是window。
function foo(){
  name = "前端曰";
}
// 其实是把name变量挂载在window对象上
function foo(){
  window.name = "前端曰";
}

// 又或者
function foo(){
  this.name = "前端曰";
}
foo() // 其实这里的this就是指向的window对象

这样无意中一个意外的全局变量就被创建了,为了阻止这种错误发生,在你的Javascript文件最前面添加 "use strict;" 。这开启了解析JavaScript的阻止意外全局的更严格的模式。或者自己注意好变量的定义!

1. 循环引用

在js的内存管理环境中,对象 A 如果有访问对象 B 的权限,叫做对象 A 引用对象 B。引用计数的策略是将“对象是否不再需要”简化成“对象有没有其他对象引用到它”,如果没有对象引用这个对象,那么这个对象将会被回收 。

function func() {  
    let obj1 = {};  
    let obj2 = {};  
  
    obj1.a = obj2; // obj1 引用 obj2  
    obj2.a = obj1; // obj2 引用 obj1  
}

当函数 func 执行结束后,返回值为 undefined,所以整个函数以及内部的变量都应该被回收,但根据引用计数方法,obj1 和 obj2 的引用次数都不为 0,所以他们不会被回收。要解决循环引用的问题,最好是在不使用它们的时候手工将它们设为空。

解决方案:obj1obj2 都设为 null

2. 老生常谈的闭包

闭包:匿名函数可以访问父级作用域的变量。

var names = (function(){  
    var name = "js-say";
    return function(){
        console.log(name);
    }
})()

闭包会造成对象引用的生命周期脱离当前函数的上下文,如果闭包如果使用不当,可以导致环形引用(circular reference),类似于死锁,只能避免,无法发生之后解决,即使有垃圾回收也还是会内存泄露。

3. 被遗忘的延时器/定时器

在我们的日常需求中,可能会经常试用到 setInterval/setTimeout ,但是使用完之后通常忘记清理。

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

setInterval/setTimeout 中的 this 指向的是window对象,所以内部定义的变量也挂载到了全局;if 内引用了 someResource 变量,如果没有清除 setInterval/setTimeout 的话someResource 也得不到释放;同理其实 setTimeout 也一样。所以我们用完需要记得去 clearInterval/clearTimeout

4. DOM引起的内存泄露

未清除DOM引用

var refA = document.getElementById("refA");
document.body.removeChild(refA);
// #refA不能回收,因为存在变量refA对它的引用。将其对#refA引用释放,但还是无法回收#refA。

解决方案:refA = null

DOM对象添加的属性是一个对象的引用

var MyObject = {}; 
document.getElementById("myDiv").myProp = MyObject;

解决方案:在页面 onunload 事件中释放 document.getElementById("myDiv").myProp = null;

DOM被删除或清空没有清楚绑定事件这种情况应该是比较常见的,同时也应该是比较容易被忽略的。

给DOM对象绑定事件

var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
    document.getElementById("myDiv").innerHTML = "wechat: js-say"; 
}

document.body.removeChild(btn);
btn = null;

这里把DOM移除了,但是绑定的事件仍没被移除,会引起内存泄露所以需要清除事件。

var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
  btn.onclick = null;
    document.getElementById("myDiv").innerHTML = "wechat: js-say"; 
}

document.body.removeChild(btn);
btn = null;
小广告

我自己运营的公众号,记录我自己的成长!

每周至少一篇博客,拒绝拖延从我做起!

公众号:前端曰

公众号ID:js-say

ps:是(yue)不是(ri)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104352.html

相关文章

  • JS高程中垃圾回收机制与常见内存泄露解决方法

    摘要:解决方式是,当我们不使用它们的时候,手动切断链接淘汰把和对象转为了真正的对象,避免了使用这种垃圾收集策略,消除了以下常见的内存泄漏的主要原因。以上参考资料高程垃圾收集类内存泄漏及如何避免内存泄露及解决方案详解类内存泄漏及如何避免 showImg(http://ww1.sinaimg.cn/large/005Y4rCogy1ft1ikzcqzqj30ka0et77a.jpg); 前言 起...

    kidsamong 评论0 收藏0
  • web 应用常见安全漏洞一览

    摘要:应用常见安全漏洞一览注入注入就是通过给应用接口传入一些特殊字符,达到欺骗服务器执行恶意的命令。此外,适当的权限控制不曝露必要的安全信息和日志也有助于预防注入漏洞。 web 应用常见安全漏洞一览 1. SQL 注入 SQL 注入就是通过给 web 应用接口传入一些特殊字符,达到欺骗服务器执行恶意的 SQL 命令。 SQL 注入漏洞属于后端的范畴,但前端也可做体验上的优化。 原因 当使用外...

    darkerXi 评论0 收藏0
  • web 应用常见安全漏洞一览

    摘要:应用常见安全漏洞一览注入注入就是通过给应用接口传入一些特殊字符,达到欺骗服务器执行恶意的命令。此外,适当的权限控制不曝露必要的安全信息和日志也有助于预防注入漏洞。 web 应用常见安全漏洞一览 1. SQL 注入 SQL 注入就是通过给 web 应用接口传入一些特殊字符,达到欺骗服务器执行恶意的 SQL 命令。 SQL 注入漏洞属于后端的范畴,但前端也可做体验上的优化。 原因 当使用外...

    Panda 评论0 收藏0
  • Android性能优化之内存优化

    摘要:导语智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是阵营,从一开始的一两百到今天动辄,内存。恰好最近做了内存优化相关的工作,这里也对内存优化相关的知识做下总结。 导语 智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是Android阵营,从一开始的一两百M到今天动辄4G,6G内存。然而大部分的开发者观看下自己的异常上报系...

    cheng10 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<