• JS中4种常见的内存泄漏


    一、什么是内存泄漏

    本质上讲,内存泄漏是当一块内存不再被应用程序使用的时候,由于某种原因,这块内存没有返还给操作系统或空闲内存池的现象。

    二、几种常见的内存泄漏

    1、意外的全局变量

    一个未声明变量的引用会在全局对象中创建一个新的变量。在浏览器的环境下,全局对象就是window,也就是说:

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

    实际上是:

    function foo(arg) {
        window.bar = "this is an explicit global variable";
    }

    上面代码中,如果bar是一个应该指向foo函数作用域内变量的引用,但忘记使用var来声明这个变量,这时就相当于创建了一个全局变量。

    另外一种偶然创建全局变量的方式如下:

    function foo() {
        this.variable = "potential accidental global";
    } 
    foo();

    上面代码中,foo函数再全局作用域中被调用,因此this指向window

    全局变量的注意事项:

    如果需要全局变量来存储很多数据,必须确保在使用过后将它设置为null或重新为他赋值。

    常见的和全局变量相关的引发内存消耗增长的原因是缓存。缓存存储着可复用的数据。

    为了让这种做法更高效,必须为缓存的容量规定一个上界。由于缓存不能被及时回收的缘故,缓存无限制地增长会导致很高的内存消耗。

    2、闭包引起的内存泄漏

    闭包可以使变量常驻内存,但如果使用不当就会在成内存泄漏

    var theThing = null;
    var replaceThing = function () {
      var originalThing = theThing;
      var unused = function () {
        if (originalThing)
          console.log("hi");
      };
      theThing = {
        longStr: new Array(1000000).join('*'),
        someMethod: function () {
          console.log(someMessage);
        }
      };
    };
    setInterval(replaceThing, 1000);

    上面代码中,每次调用 replaceThing 时,theThing 都会得到新的包含一个大数组和新的闭包(someMethod)的对象。

    同时,没有用到的那个变量持有一个引用了 originalThingreplaceThing 调用之前的 theThing)闭包。

    关键的问题是每当在同一个父作用域下创建闭包作用域的时候,这个作用域是被共享的。在这种情况下,someMethod 的闭包作用域和 unused 的作用域是共享的。

    unused 持有一个 originalThing 的引用。尽管 unused 从来没有被使用过,someMethod 可以在 theThing 之外被访问。

    而且 someMethod 和 unused 共享了闭包作用域,即便 unused 从来都没有被使用过,它对 originalThing 的引用还是强制它保持活跃状态(阻止它被回收)。

    当这段代码重复运行时,将可以观察到内存消耗稳定地上涨,并且不会因为 GC 的存在而下降。

    本质上来讲,创建了一个闭包链表(根节点是 theThing 形式的变量),而且每个闭包作用域都持有一个对大数组的间接引用,这导致了一个巨大的内存泄露。

    3、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'))  
    }  

    2、被遗漏的定时器和回调函数

    var someResouce=getData();  
    setInterval(function(){  
        var node=document.getElementById('Node');  
        if(node){  
            node.innerHTML=JSON.stringify(someResouce)  
        }  
    },1000) 

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

    三、怎样避免内存泄漏

    1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;

    2)注意程序逻辑,避免“死循环”之类的 ;

    3)避免创建过多的对象  原则:不用了的东西要及时归还。

  • 相关阅读:
    测试次数--蓝桥杯
    承压计算--蓝桥杯
    天梯赛--连续因子
    等差素数数列-蓝桥杯
    hdu-1237-简单计算器
    hdu-1022-栈
    [BZOJ3172]:[Tjoi2013]单词(AC自动机)
    [BZOJ4327]:[JZOI2012]玄武密码(AC自动机)
    [HDU5360]:Gorgeous Sequence(小清新线段树)
    [BZOJ3307]:雨天的尾巴(LCA+树上差分+权值线段树)
  • 原文地址:https://www.cnblogs.com/endlessmy/p/8688056.html
Copyright © 2020-2023  润新知