百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>JS教程> 一张纸搞懂JS系列(3)之垃圾回收机制,内存走漏,闭包
分享文章到:

一张纸搞懂JS系列(3)之垃圾回收机制,内存走漏,闭包

发布时间:10/01 来源:未知 浏览: 关键词:

概览

  • 食用时间: 6-12分钟
  • 难度: 简便,别跑,看完再走

垃圾回收机制

前面一篇博客主要讲解了内存的分配和使用(栈内存与堆内存,深拷贝与浅拷贝),使用完了今后,当然是要将不使用的内存偿还,就像将手机上不使用的软件从后台清除,可以晋升手机的运转速度,不然越来越多,迟早会卡, JS 也是一样的。

每隔一段时间, JS垃圾收集器都会对变量停止“巡查”,就和保安巡查园区一样,让不相关的人抓紧走。当一个变量不被需要了今后,它就会把这个变量所占用的内存空间所开释,这个历程就叫做垃圾回收

JS 的垃圾回收算法分为两种,援用计数法和标志清除法

  • 援用计数法

    援用计数法是最初级的垃圾回收算法,已经被现代阅读器所裁汰了。在学习援用计数法此前,需要第一对援用有必然的概念,你可以认为它就是对当前变量所指向的那块内存地址的描写,有点相似于JS援用数据类型的内存指向的概念,先来看一行代码:

    var obj={name:'jack'};复制代码

    当我们在给 obj 赋值的同时,其实就创立了一个指向该变量的援用,援用计数为1,在援用计数法的机制下,内存中的每一个值都会对应一个援用计数

    而当我们给 obj 赋值为 null时,这个变量就变成了一块没用的内存,那么此时, obj 的援用计数将会变成 0,它将会被垃圾收集器所回收,也就是 obj 所占用的内存空间将会被开释

    我们知道,函数作用域的生命周期是很短暂的,在函数施行完毕之后,里面的变量根本是没用的变量了,不清除的后果就是该内存垃圾没有被开释,仍然并吞着原有的内存不松手,就会容易激发内存走漏,先来看一段代码乃至运转结果:

    function changeName(){   var obj1={};   var obj2={};
       
       obj1.target=obj2;
       obj2.target=obj1;
       obj1.age=15;   console.log(obj1.target);   console.log(obj2.target);
    }
    
    changeName();复制代码
    我们可以看到, obj1.targetobj2.target 存在互相援用的状况,由于在改动 obj1.age 的同时,obj1.target.ageobj2.target.age 也同时都被影响到了,它们所指向的援用计数是一致的

    在函数施行完毕的时候, obj1obj2 还是活的好好地,由于 obj1.targetobj2.target 的援用计数在施行完毕之后,依然是 1 ,明明函数施行完毕,但是这种垃圾仍然存在,这种函数定义多了,内存走漏也会是没法幸免的

  • 标志清除法

    上面的援用计数法的弊端已经很明显了,那么,此刻所要说的标志清除法就不存在这模样的问题。由于它采纳的推断标准是看这个对象可否可到达,它主要分为两个阶段,标志阶段清除阶段:

    • 标志阶段

      垃圾收集器会从根对象(Window对象)动身,扫描所有可以触及的对象,这就是所谓的可到达

    • 清除阶段 在扫描的同时,根对象没法触及(不成到达)的对象,就是被认为不被需要的对象,就会被当做垃圾清除

    此刻再来看下上面的代码

    function changeName(){    var obj1={};  var obj2={};
      
      obj1.target=obj2;
      obj2.target=obj1;
      obj1.age=15;  console.log(obj1.target);  console.log(obj2.target);
    }
    
    changeName();复制代码

    在函数施行完毕之后,函数的声明周期完毕,那么此刻,从 Window对象 动身, obj1obj2 都会被垃圾收集器标志为不成到达,这模样的状况下,互相援用的状况也会瓜熟蒂落。

内存走漏

该开释的内存垃圾没有被开释,仍然并吞着原有的内存不松手,造成系统内存的白费,致使机能恶化,系统崩溃等严峻后果,这就是所谓的内存走漏

闭包

  • 定义与特性

    闭包是指有权拜访另一个函数作用域中的变量的函数。至于为什么有权拜访,主如果由于作用域嵌套作用域,也就是所谓的作用域链,关于作用域链不分明的可以看我的第一篇博客一文搞懂JS系列(一)之编译道理,作用域,作用域链,变量晋升,临时性死区,就是由于作用域链的存在,所以内部函数才可以拜访外部函数中定义的变量 ,作用域链是向外不向内的,探出头去,向外查寻,而不是看着锅里,所之外部函数是没法拜访内部函数定义的变量的。并且,还有一个特性就是将闭包内的变量始终保持在内存中。

    前面的作用域向外不向内,这里就不再做过多说明了,我们主要来看我后面说的特性,那就是闭包内的变量始终留存在内存中

    来看一下阮一峰教程傍边的一个例子

     function f1(){     var n=999;
    
         nAdd=function(){n+=1}     function f2(){         console.log(n);
         }     return f2;
    
     } var result=f1();     //等同于return f2();
    
     result(); // 999
    
     nAdd();
    
     result(); // 1000
     nAdd();
    
     result(); // 1000复制代码

    从输出结果就可以看得出来,这个变量 n 就不断留存在内存中,那么,为什么会这模样呢,我们此刻就来逐渐地剖析代码

    ① 第一 f1() 作为 f2() 的父函数,按照作用域链的规则, nAdd() 办法乃至 f2() 办法中可以正常拜访到 n 的值

    f2() 被给予了一个全局变量,大概这里大家就会开端发生迷惑了,这个 f2() 不是好好地定义在了 f1() 函数中吗,这不是扯淡吗,那么,先看下面的这句 var result=f1(); ,这个 result 很明显是被给予了一个全局变量,这应当是没有任何争议的,那么,接着来看这个 f1() ,可以看到最后,是一句 return f2; ,看到这里,也许大家也已经想清楚了,这个 f2() 被给予了一个全局变量

    ③ 已经清楚了上面的这一点今后,按照上面垃圾回收机制所说起到的标志清除法,这个 f2() 始终是可以被根对象 Window 拜访到的,所以 f2 将始终存在于内存之中,而 f2 是依靠于 f1 ,因此 f1 也将始终存在于内存傍边,那么, n 的值也就天然始终存在于内存傍边啦

    ④ 还有一点需要留意的就是为什么我们可以直接施行 nAdd() ,这是由于在 nAdd() 的前面没有使用 var ,因此 nAdd() 是一个全局函数而不是部分函数

    所以,闭包的变量会常驻内存,滥用闭包涵易造成内存走漏,特殊是在 IE 阅读器下,2020年了,应当没人使用 IE 了吧(小声bb),解决方法就是在退出函数此前,将不使用的部分变量全部删除,这也是上面讲了垃圾回收机制 => 内存走漏,再讲到闭包的缘由,我会尽量将有关联性的知识点一起讲了,也利便大家学习和加深印象。

系列名目

  • 一张纸懂JS系列(1)之编译道理,作用域,作用域链,变量晋升,临时性死区

  • 一张纸搞懂JS系列(2)之JS内存生命周期,栈内存与堆内存,深浅拷贝

  • 一张纸搞懂JS系列(3)之垃圾回收机制,内存走漏,闭包

相关免费学习引荐:javascript(视频)

以上就是一张纸搞懂JS系列(3)之垃圾回收机制,内存走漏,闭包的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有152人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板