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

  • 相关阅读:
    JWT(JSON WEB TOKEN) / oauth2 / SSL
    Guice 学习
    九 fork/join CompletableFuture
    二 lambda表达式
    IDEA 热部署 + 下载jar包放到maven中
    微服务学习一 微服务session 管理
    一 Optional
    八 线程池(待续)
    七 内置锁 wait notify notifyall; 显示锁 ReentrantLock
    六 多线程问题
  • 原文地址:https://www.cnblogs.com/KruceCoder/p/10690185.html
Copyright © 2011-2022 走看看