首页 > 编程知识 正文

js垃圾回收机制和v8,js垃圾回收机制标记清除

时间:2023-05-04 07:48:16 阅读:191405 作者:4657

Javascript垃圾回收机制GC(Garbage Collecation)                                                                                                                                                   --之浅浅浅浅浅浅浅浅浅析

 

                                                                                                    导读

    Javascript中的内存周期有三部分(分配,使用,回收),这里主要是讲最后一部分的回收。前端开发人员在学习,编码或做其他方面的时候很少会考虑到内存管理和内存空间概念,我也曾一度以为JS作为高级编程语言有自动管理内存的机制,那在学习JS的过程中,内存相关的学习不是那么重要的。

     在这里我想问自己和大家几个JS的问题:深拷贝和浅拷贝有什么不同?引用数据类型和引用传递是怎么回事?闭包到底是怎么产生的?原型又是啥?

      其实在了解JS的内存机制后,上述问题稍加思索都迎刃而解,同时对自己的代码和他人的代码在内存这一块也能有更深的理解,也对我们解决一些JS实际问题的帮助是匪浅和深远的。这篇分享里的内容只是冰山一角,它旨在让大家对GC有个笼统的认知。

   首先简述js两种变量类型以及它们的内存分配方式,再简单介绍下堆和栈;

   然后灌输两个定义和一个概念,它们将是判断一个引用对象是否会被回收的重要依据;

   再举例说明帮助理解定义和概念;

   之后简述js内部回收机制的一个基本算法的基础原理和流程,并列举其他几种回收算法;

   // 最后大概的说一下,GC在什么时候对什么东西做了什么;

注:里面有些纯属于个人理解,如有错误和遗漏之处,敬请指正。

 

                                                   一

         和JAVA的JVM一样,JS对一个对象的内存初始分配,使用中的维护,最后的回收,也是由解释器(现在,解释演变为了JIT,V8就是其中之一)自动处理的,不需要考虑这个对象之后回收操作,也不需要在使用的时候对它进行维护。这就大大的防止了出现内存泄露和溢出的情况,也将我们从繁杂的内存(堆)管理中解放了出来,可以更多地去关注其他重点。

    在JS中临时存储数据的地方有堆和栈。栈一般存放函数的参数值,局部变量值等;堆一般存放定义的对象等;

         栈(栈内存):自动分配相对固定大小的内存空间,并由系统自动释放

         堆(堆内存):动态分配内存,内存大小不一(堆数据的地址指针存放于栈中)

JS中的变量从内存分配方式(堆栈)区分,可分为基本数据类型和引用数据类型:

         基本数据类型:Undefined,Null,Boolean,Number,String这五种数据类型的值都是直接存储在栈区的,在栈区占用的空间大小是确定,由系统直接管理。相对于堆来说可以更加快速和便捷地被管理

                            在操作基本数据类型的时候,因为是按值访问的,所以会对值直接进行操作:

                                          b的值2会被直接修改为1

                                                                  

              引用数据类型:Object(包括Array和Function等其他Object的派生类型)是存储在堆中的,不过它的地址指针是存储在栈中,所以我们在实际访问的时候也是要先去栈中找到对应的指针,然后根据指针在堆中取出对应的数据

                                                                 

                                           

                                                                      

将变量a赋值给b实质上是复制了在栈中的引用指针,这两个指针都是指向堆中的同一个对象值,那a和b引用的是同一个对象,那么很容易就能理解平常写代码时用到的(深度)拷贝是为了什么。为了什么?

                                    

              这里的分配建议可以仔细思考下一下,对深浅拷贝的区别会有恍然大悟的感觉。

 

                                                    二

上面JS解释器已经自动分配好了内存,那么在我们使用的时候它是怎么维护这些内存的呢,它在我们使用这些值的时候做了什么?或者说在之后的回收处理中,它是如何知道哪些对象是需要回收的呢?

                                                          二.一

对象是否需要回收,取决于它是否处于可达性(概念)的状态(IE并不适用这个概念)。处于非可达性的对象在一定条件下会被GC回收。

可达性简单来说就是:那些以某些方式可以访问的值,它们会被保证存储在内存中的状态,称为可达性--是判断一个对象是否会被回收的重要依据。

可达性稍微详细点来说就是:从根属性能访问到的可访问的对象的状态

根:函数的局部变量,参数,全局变量所指向的对象称为根对象

可访问:如果引用或引用链可以从根访问任何其他值,则认为该值是可访问的

那我们重新来理解一下对象是否需要回收的判断依据:一个对象处于活跃状态,且当它能被一个根对象或另一个活跃对象指向,此时它不会被回收。反之回收

                                                   首先定义一个全局变量:singer 为 {name:'陈小春'}

                                                            

这个时候如果一直不改变这个值,{name:'陈小春'}这一块内存就一直不会被GC回收;如果想让GC后期回收它,需要让它处于不可达,既从根上访问不到,此时的根为全局环境,那么:

                                                                      

                                  在下一次GC运行监测到从全局访问不到{name:'陈小春'}这一块内存,就会回收

                                      

                                                                                        再次定义

                                                                       

                                                              此时两个变量都指向{name:'陈小春'}这一块内存

                                                                          

                                                    

                          如果仅仅将actor修改为null,{name:'陈小春'}这一块内存还是可以由singer访问到,所以不会被回收

                                         

看一个复杂点的

在下一次GC运行时,我们打算在这个family里回收{name:”ngdbq”}。应该怎么做?让他们离婚,并且让mother脱离family,既移除family中mother变量对{name:”ngdbq”}的引用,移除{name:'陈小春',wife:{name:”ngdbq”}}中,wife对{name:”ngdbq”}的引用

 

 

 

                                                              二.二

判断依据已经知道了,那解释器回收一个对象会做一些什么操作呢(只针对现代浏览器,排除IE的回收)?会依据“标记-清除”算法执行GC

“标记-清除”算法:

--- 首先获取根,并且标记它们。

--- 然后访问根的引用,并且标记它们。

--- 然后访问根引用的引用,并标记它们。

--- 一直访问下去,直到标记完能从根访问到的所有对象。

--- 除去标记的对象,其余的对象都被回收。

这看起来就很麻烦,稍微一想,我们可能会提出一些问题:

    这个标记是什么?它本身会不会占用内存?当我们在堆内存中创建了大量的对象时,标记的动作和标记本身都可能会消耗很多资源,这个是怎么解决的?标记动作进行的时候,会不会阻断JS的运行,增加JS的运行时长?还有诸如此类的问题。

    所以在现有的GC中除了“标记-清除”算法,还有以下一些算法同时进行工作去优化以上问题:

分代回收——对象分为两组:“新对象”和“旧对象”。许多对象出现,完成它们的工作并迅速结 ,它们很快就会被清理干净。那些活得足够久的对象,会变“老”,并且很少接受检查。增量回收——如果有很多对象,并且我们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有一定的延迟。因此,引擎试图将垃圾回收分解为多个部分。然后,各个部分分别执行。这需要额外的标记来跟踪变化,这样有很多微小的延迟,而不是很大的延迟。空闲时间收集——垃圾回收器只在 CPU 空闲时运行,以减少对执行的可能影响。

GC是双刃剑,过多的GC也会消耗很多资源,所以在编码中要劲量避免一些不必要操作,引起多余的GC。这又是另一个要研究的知识点了,不过了解GC机制是前提。上面的可达性,一定程度上可以在编码的时候帮助我们判断。

   

以上内容非常浅显,建议前端同学有时间可以fqdds好好看看相关内容,下面有个推荐的链接,讲得很好。

深入了解:http://newhtml.net/v8-garbage-collection/

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。