最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深入了解 JavaScript 内存泄露

    正文概述 掘金(Gaby)   2021-08-07   461

    这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

    这篇文章是针对浏览器的 JavaScript 脚本,Node.js 大同小异,这里不涉及到 Node.js 的场景。当然 Node.js 作为服务端语言,必然更关注内存泄漏的问题。

    用户一般不会在一个 Web 页面停留太久,即使有一点内存泄漏,重载页面内存也会跟着释放。而且浏览器也有自动回收内存的机制,所以也不会特别关注内存泄漏的问题。

    但是作为开发人员的我们如果对内存泄漏没有什么概念,有时候还是有可能因为内存泄漏,导致页面卡顿。了解了内存泄漏,就知道该如何避免内存泄漏,这也是我们提升前端技能的必经之路。

    目录

    内存的概念

    内存的生命周期

    内存泄漏是如何产生的

    造成内存泄漏的场景

    如何查找内存泄漏


    内存的概念

    内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。内存(Memory)也被称为[内部存储器]其作用是用于暂时存放CPU中的运算数据,以及与[硬盘]等[外部存储器]交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。 内存是由[内存芯片]、电路板、[金手指]等部分组成的。

    内存的生命周期

    内存也是有生命周期的,不管什么程序语言,一般可以按顺序分为三个周期:

    • 分配期

      分配所需要的内存

    • 使用期

      使用分配到的内存(读、写)

    • 释放期

      不需要时将其释放和归还

    内存分配 -> 内存使用 -> 内存释放。

    内存泄漏是如何产生的

    造成内存泄漏的场景

    如何查找内存泄漏

    什么是内存泄漏?

    如果内存不需要时,没有经过生命周期的释放期,那么就存在内存泄漏

    内存泄漏简单理解:无用的内存还在占用,得不到释放和归还。比较严重时,无用的内存会持续递增,从而导致整个系统卡顿,甚至崩溃。

    JavaScript 内存管理机制

    JavaScript 内存管理机制和内存的生命周期是一一对应的。首先需要分配内存,然后使用内存,最后释放内存

    其中 JavaScript 语言不需要程序员手动分配内存,绝大部分情况下也不需要手动释放内存,对 JavaScript 程序员来说通常就是使用内存(即使用变量、函数、对象等)。

    内存分配

    JavaScript 定义变量就会自动分配内存的。我们只需了解 JavaScript 的内存是自动分配的就足够了

    看下内存自动分配的例子:

    // 给数值变量分配内存
    let number = 123; 
    // 给字符串分配内存
    const string = "xianshannan"; 
    // 给对象及其包含的值分配内存
    const object = {
      a: 1,
      b: null
    }; 
    // 给数组及其包含的值分配内存(就像对象一样)
    const array = [1, null, "abra"]; 
    // 给函数(可调用的对象)分配内存
    function func(a){
      return a;
    } 
    

    内存使用

    根据上面的内存自动分配例子,我们继续内存使用的例子:

    // 写入内存
    number = 234;
    // 读取 number 和 func 的内存,写入 func 参数内存
    func(number);
    

    内存回收

    前端界一般称垃圾内存回收为 GC(Garbage Collection,即垃圾回收)。

    内存泄漏一般都是发生在这一步,JavaScript 的内存回收机制虽然能回收绝大部分的垃圾内存,但是还是存在回收不了的情况,如果存在这些情况,需要我们手动清理内存。

    以前一些老版本的浏览器的 JavaScript 回收机制没那么完善,经常出现一些 bug 的内存泄漏,不过现在的浏览器基本都没这些问题了,已过时的知识这里就不做深究了。

    这里了解下现在的 JavaScript 的垃圾内存的两种回收方式,熟悉下这两种算法可以帮助我们理解一些内存泄漏的场景。

    引用计数垃圾收集

    看下下面的例子,“这个对象”的内存被回收了吗?

    // “这个对象”分配给 a 变量
    var a = {
      a: 1,
      b: 2,
    }
    // b 引用“这个对象”
    var b = a; 
    // 现在,“这个对象”的原始引用 a 被 b 替换了
    a = 1;
    

    当前执行环境中,“这个对象”内存还没有被回收的,需要手动释放“这个对象”的内存(当然是还没离开执行环境的情况下),例如:

    b = null;
    // 或者 b = 1,反正替换“这个对象”就行了
    

    这样引用的"这个对象"的内存就被回收了。

    ES6 把引用有区分为强引用弱引用,这个目前只有再 Set 和 Map 中才有。

    强引用才会有引用计数叠加,只有引用计数为 0 的对象的内存才会被回收,所以一般需要手动回收内存(手动回收的前提在于标记清除法还没执行,还处于当前执行环境)。

    弱引用没有触发引用计数叠加,只要引用计数为 0,弱引用就会自动消失,无需手动回收内存。

    标记清除法

    环境可以理解为我们的作用域,但是全局作用域的变量只会在页面关闭才会销毁。

    // 假设这里是全局变量
    // b 被标记进入环境
    var b = 2;
    function test() {
      var a = 1;
      // 函数执行时,a 被标记进入环境
      return a + b;
    }
    // 函数执行结束,a 被标记离开环境,被回收
    // 但是 b 就没有被标记离开环境
    test();
    

    JavaScript 内存泄漏的一些场景

    JavaScript 的内存回收机制虽然能回收绝大部分的垃圾内存,但是还是存在回收不了的情况。程序员要让浏览器内存泄漏,浏览器也是管不了的。

    下面有些例子是在执行环境中,没离开当前执行环境,还没触发标记清除法。所以你需要读懂上面 JavaScript 的内存回收机制,才能更好理解下面的场景。

    意外的全局变量

    // 在全局作用域下定义
    function count(number) {
      // basicCount 相当于 window.basicCount = 2;
      basicCount = 2;
      return basicCount + number;
    }
    

    不过在 eslint 帮助下,这种场景现在基本没人会犯了,eslint 会直接报错,了解下就好。

    被遗忘的计时器

    无用的计时器忘记清理是新手最容易犯的错误之一。

    就拿一个 vue 组件来做例子。

    <template>
      <div></div>
    </template>
    
    <script>
    export default {
      methods: {
        refresh() {
          // 获取一些数据
        },
      },
      mounted() {
        setInterval(function() {
          // 轮询获取数据
          this.refresh()
        }, 2000)
      },
    }
    </script>
    

    上面的组件销毁的时候,setInterval 还是在运行的,里面涉及到的内存都是没法回收的(浏览器会认为这是必须的内存,不是垃圾内存),需要在组件销毁的时候清除计时器,如下:

    <template>
      <div></div>
    </template>
    
    <script>
    export default {
      methods: {
        refresh() {
          // 获取一些数据
        },
      },
      mounted() {
        this.refreshInterval = setInterval(function() {
          // 轮询获取数据
          this.refresh()
        }, 2000)
      },
      beforeDestroy() {
        clearInterval(this.refreshInterval)
      },
    }
    </script>
    

    被遗忘的事件监听器

    无用的事件监听器忘记清理是新手最容易犯的错误之一。

    还是继续使用 vue 组件做例子。

    <template>
      <div></div>
    </template>
    
    <script>
    export default {
      mounted() {
        window.addEventListener('resize', () => {
          // 这里做一些操作
        })
      },
    }
    </script>
    

    上面的组件销毁的时候,resize 事件还是在监听中,里面涉及到的内存都是没法回收的(浏览器会认为这是必须的内存,不是垃圾内存),需要在组件销毁的时候移除相关的事件,如下:

    <template>
      <div></div>
    </template>
    
    <script>
    export default {
      mounted() {
        this.resizeEventCallback = () => {
          // 这里做一些操作
        }
        window.addEventListener('resize', this.resizeEventCallback)
      },
      beforeDestroy() {
        window.removeEventListener('resize', this.resizeEventCallback)
      },
    }
    </script>
    

    文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞?收藏加关注?,希望点赞多多多多...


    起源地下载网 » 深入了解 JavaScript 内存泄露

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元