美文网首页
JS的垃圾回收机制与常见内存泄露的解决方法

JS的垃圾回收机制与常见内存泄露的解决方法

作者: Michael113c | 来源:发表于2022-05-29 19:58 被阅读0次

    因为项目需要涉及到了垃圾回收机制和内存泄漏,查看网上了许多教程,未免以后遗忘故此记录下来以便以后查阅。

    在不需要字符串、对象的时候,需要释放其所占用的内存,否则将会消耗完系统中所有可用的内存,造成系统崩溃,这就是垃圾回收机制所存在的意义

    所谓的内存泄漏指的是:由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。

    垃圾回收

    javascript不同于c、c++的一个特点是:具有自动的垃圾回收机制,内存的分配以及内存的回收完全实现了自动管理,减少了许多不必要的麻烦。

    所谓的垃圾回收就是找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。
    全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收

    实现垃圾回收的方式主要有两种:标记清除,.引用计数

    1.标记清除

    目前最常用的垃圾回收的方式,也是当前浏览器所采用的垃圾收集策略。

    原理

    在标记清除的方式中有两个概念:『进入环境』和『离开环境』。『进入环境』指变量进入执行的环境。『离开环境』指变量完成任务,离开了执行的环境。

    当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

    流程

    1. 垃圾收集器在运行的时候给存储在内存中的所有变量都加上标记
    2. 去掉环境中的变量以及被环境中的变量引用的变量的标记
    3. 那些还存在标记的变量则被视为准备删除的变量。
    4. 最后垃圾收集器会执行内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间

    2.引用计数

    原理:
    引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。
    相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这 个值了,因而就可以将其占用的内存空间回收回来。
    这样,当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存。

    不过这里潜在的一个问题是:循环引用时,两个对象都至少被引用了一次,将不能自动被回收。所以导致,我们常讲的内存泄露。所以这种机制在js中并不常用。

    『循环引用』指的是对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。对于像js类的自动回收机制的语言来说,需要额外手动的去释放内存,其实并不友好,例如,在下面的例子中:

    function ftc(){
        var A = new Object();
        var B = new Object();
        A.property = B; 
        B.property = A;
    }
    

    内存泄漏的原因

    虽然js有垃圾回收机制,但我们在编写代码的时候,有些情况还是会造成内存泄漏,了解这些情况,并在编写程序的时候,注意避免,我们的程序会更加完善。

    1.上文我们提到了全局变量不会被当成垃圾回收,我们在编码中有时会出现下面这种情况:

     function foo() {
         this.bar2 = '默认绑定this指向全局' // 全局变量=> window.bar2
          bar = '全局变量'; // 没有声明变量 实际上是全局变量=>window.bar
       }
     foo();
    

    当我们使用默认绑定,this会指向全局,this.something也会创建一个全局变量,这一点可能很多人没有注意到。

    解决方法:在函数内使用严格模式or细心一点

    function foo() {
          "use strict"; 
          this.bar2 = "严格模式下this指向undefined"; 
          bar = "报错";
        }
        foo();
    

    2.当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。

    var someResource = getData();
    setInterval(function() {
        var node = document.getElementById('Node');
        if(node) {
            node.innerHTML = JSON.stringify(someResource));
            // 定时器也没有清除
        }
        // node、someResource 存储了大量数据 无法回收
    }, 1000);
    

    解决方法: 在定时器完成工作的时候,手动清除定时器。

    3.循环引用问题,上文有提到

    4.没有清理DOM元素引用:
    html:

    <div id="test">
        <span>1</span>
        <span>2</span>
      </div>
    

    js:

    var refA = document.getElementById("test");
    document.body.removeChild(refA); // dom删除了
    console.log(refA, "refA");  // 但是还存在引用 能console出整个div 没有被回收
    refA = null;//解决方法
    console.log(refA, "refA");  // 解除引用
    

    5.console保存大量数据在内存中。过多的console,比如定时器的console会导致浏览器卡死。

    如何避免内存泄漏

    1. 减少不必要的全局变量,使用严格模式避免意外创建全局变量。
    2. 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。
    3. 组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。

    相关文章

      网友评论

          本文标题:JS的垃圾回收机制与常见内存泄露的解决方法

          本文链接:https://www.haomeiwen.com/subject/eyngprtx.html