Skip to content

JS中的垃圾回收

Posted on:August 19, 2022

浏览器怎么进行垃圾回收?

  1. 什么是垃圾
  1. 如何捡垃圾
  1. 什么时候捡垃圾

浏览器中不同类型变量的内存都是何时释放?

Javascritp 中类型:值类型,引用类型。

  1. 引用类型

    在没有引用之后,通过 V8 自动回收。

  2. 值类型

    如果处于闭包的情况下,要等闭包没有引用才会被 V8 回收。 非闭包的情况下,等待 V8 的新生代切换的时候回收。

哪些情况会导致内存泄露?如何避免?

  1. 意外的全局变量

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

    bar 没被声明,会变成一个全局变量,在页面关闭之前不会被释放。

    另一种意外的全局变量可能由  this  创建:

    function foo() {
      this.variable = "potential accidental global";
    }
    // foo 调用自己,this 指向了全局对象(window)
    foo();

    在 JavaScript 文件头部加上use strict,可以避免此类错误发生。启用严格模式解析 JavaScript,避免意外的全局变量。

  2. 被遗忘的计时器或回调函数

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

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

  3. 闭包

    function bindEvent() {
      var obj = document.createElement("xxx");
      obj.onclick = function () {
        // Even if it is a empty function
      };
    }

    闭包可以维持函数内局部变量,使其得不到释放。上例定义事件回调时,由于是函数内定义函数,并且内部函数的事件回调引用外部函数,形成了闭包。可以将事件处理函数定义在外面,或者在定义事件处理函数的外部函数中,删除对 dom 的引用:

    // 将事件处理函数定义在外面
    function bindEvent() {
      var obj = document.createElement("xxx");
      obj.onclick = onclickHandler;
    }
    
    // 或者在定义事件处理函数的外部函数中,删除对dom的引用
    function bindEvent() {
      var obj = document.createElement("xxx");
      obj.onclick = function () {
        // Even if it is a empty function
      };
      obj = null;
    }
  4. 没有清理的 DOM 元素引用 有时保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成 JSON 键值对或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 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"));
      // 此时,仍旧存在一个全局的 button 的引用
      // elements对象,button 元素仍旧在内存中,不能被 GC 回收。
      // 执行elements["button"] = null
    }

    虽然我们用removeChild移除了 button,但是还在elements对象里保存着 button 的引用,换言之,DOM 元素还在内存里面。