zoukankan      html  css  js  c++  java
  • 关于JavaScript的内存泄漏的思考

    1. 概念

      1) 内存泄漏就是指程序中不再用到的对象依然占用的内存无法释放;

      2) 程序中的内存过程:系统分配------程序使用 ------ 程序、系统释放

    说到内存泄漏不得不提到垃圾回收机制

    2. 垃圾回收机制

    目前垃圾回收机制有两种:

      1) 引用标记法  

        优势:简单

        劣势:对于循环引用的对象无法清除

      2) 标记清除法--解决了循环引用对象

    3. 常见的内存泄漏

    1)全局变量

      对于定义的全局变量,由于挂在window上,除非刷新浏览器,这个变量就永远不会被回收

    2)未销毁的定时器和回调函数

    var serverData = loadData();
    setInterval(function() {
        var renderer = document.getElementById('renderer');
        if(renderer) {
            renderer.innerHTML = JSON.stringify(serverData);
        }
    }, 5000); // 每 5 秒调用一次

      如果后续renderer元素被移除,此时定时器就没有任何作用,但是如果没有清楚定时器,定时器的内存就没有被回收,这样serverData也无法被回收

    3)闭包

      在 JS 开发中, 我们会经常用到闭包, 一个内部函数, 有权访问包含其的外部函数中的变量. 下面这种情况下, 闭包也会造成内存泄露.

    var theThing = null;
    var replaceThing = function () {
      var originalThing = theThing;
      var unused = function () {
        if (originalThing) // 对于 'originalThing'的引用
          console.log("hi");
      };
      theThing = {
        longStr: new Array(1000000).join('*'),
        someMethod: function () {
          console.log("message");
        }
      };
    };
    setInterval(replaceThing, 1000);

      这段代码,每次调用replaceThing时,theThing获得了包含一个巨大的数组和一个对于新闭包someMethod的对象,同时unused是一个引用了originalThing的闭包。

        一旦为同一父作用域中的闭包创建了闭包范围,就会共享作用域。

      这个范例的关键在于,闭包之间是共享作用域,尽管unused可能一直没有被调用,但是someMethod可能会被调用,这样就会导致无法对其进行回收

      修改上述问题:

    var theThing = null; 
    var replaceThing = function(){ 
      var originalThing = theThing; 
      //定义一个引用originalThing但不会
      //实际被调用的闭包。但是因为这个闭包存在,所以
      // originalThing将在词汇环境中用于在replaceThing中定义的所有//闭包,而不是在其中进行优化
      //。如果删除此功能,则无泄漏。
      var unused = function(){ 
        if(originalThing)
          console.log(“hi”); 
      }; 
      theThing = { 
        longStr:new Array(1000000).join('*'),
        //虽然这个
        //函数理论上可以访问originalThing ,但它显然不会使用它。但是因为
        // originalThing是词法环境的一部分,someMethod 
        //将保存对originalThing的引用,所以即使我们
        //正在用无效的方式替换theThing 
        //引用旧的值ofThing,旧值
        //永远不会被清理干净!
        someMethod:function(){} 
      }; 
      //如果在这里添加`originalThing = null`,则没有泄漏。
    }; 
    setInterval(replaceThing,1000);

    4)DOM引用

      很多时候,我们对DOM的操作会把DOM的引用保存在一个数组或者Map中

    var elements = {
        image: document.getElementById('image')
    };
    function doStuff() {
        elements.image.src = 'http://example.com/image_name.png';
    }
    function removeImage() {
        document.body.removeChild(document.getElementById('image'));
        // 这个时候我们对于 #image 仍然有一个引用, Image 元素, 仍然无法被内存回收. 
    }

      上述案例中,即使我们对于image元素进行了移除,但是仍然有对image元素的引用,依然无法进行内存回收

      另外需要注意的一个点是, 对于一个 Dom 树的叶子节点的引用. 举个例子: 如果我们引用了一个表格中的 td 元素, 一旦在 Dom 中删除了整个表格, 我们直观的觉得内存回收应该回收除了被引用的 td 外的其他元素. 但是事实上, 这个 td 元素是整个表格的一个子元素, 并保留对于其父元素的引用. 这就会导致对于整个表格, 都无法进行内存回收. 所以我们要小心处理对于 Dom 元素的引用.

    参考原文:

    https://blog.sessionstack.com/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks-3f28b94cfbec

    https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156

  • 相关阅读:
    7月的尾巴,你是XXX
    戏说Android view 工作流程《下》
    “燕子”
    Android开机动画bootanimation.zip
    戏说Android view 工作流程《上》
    ViewController里已连接的IBOutlet为什么会是nil
    My first App "Encrypt Wheel" is Ready to Download!
    iOS开发中角色Role所产生的悲剧(未完)
    UIScrollView实现不全屏分页的小技巧
    Apple misunderstood my app,now my app status changed to “In Review”
  • 原文地址:https://www.cnblogs.com/KruceCoder/p/10690185.html
Copyright © 2011-2022 走看看