javascript垃圾回收机制方法,javascript垃圾回收方法

  javascript垃圾回收机制方法,javascript垃圾回收方法

  这篇文章给大家带来了一些关于javascript的知识,包括垃圾收集,这是JavaScript的隐藏机制。下面就来看看吧,希望对你有帮助。

  【相关推荐:javascript视频教程,web前端】

  

一、前言

  垃圾收集是JavaScript的隐藏机制。我们通常不用为垃圾回收而努力,只需要专注于功能的开发就可以了。但这并不意味着我们在编写JavaScript时可以高枕无忧。随着我们实现的功能越来越复杂,代码量越来越大,性能问题越来越突出。如何写出执行速度更快,占用内存更少的代码,是程序员永无止境的追求。一个优秀的程序员总是能以极其有限的资源取得惊人的成绩,这也在形式上区分了芸芸众生和崇高的神灵。

  

二、何为垃圾

  代码在计算机的内存中执行。代码中定义的所有变量、对象和函数都会在内存中占用一定的内存空间。在计算机中,内存空间是一种非常紧张的资源。我们必须时刻注意内存的占用量。毕竟内存条很贵!如果一个变量、函数或对象在创建后不再被后续的代码执行所需要,那么它就可以被称为垃圾。

  虽然很容易直观地理解垃圾的定义,但计算机程序很难在某个时刻确定目前存在的变量、函数或对象在未来不会被使用。为了减少计算机内存的开销,保证计算机程序的正常执行,我们通常规定满足以下任何一个条件的对象或变量都是垃圾:

  没有引用的对象或变量;不可达对象(多个对象之间的循环引用);未引用的变量或对象就相当于一个没有门的房子,我们永远进不去,也就不能再用了。虽然不可访问的对象之间存在连接性,但是无法从外部访问,因此无法再次使用。满足上述条件的对象或变量在程序未来的执行中永远不会再被使用,因此可以作为垃圾安全回收。

  

三、垃圾回收

  垃圾收集机制(GC,Garbage Collection mechanism)负责回收程序执行过程中无用的变量和内存占用的空间。一个对象虽然没有被重用的可能性,但仍然存在于内存中的现象,称为内存泄漏。内存泄漏是一种非常危险的现象,尤其是在长时间运行的程序中。如果一个程序有内存泄漏,它会占用越来越多的内存空间,直到耗尽内存。

  字符串、对象和数组没有固定的大小,所以只有当它们的大小已知时,才可以动态地分配存储空间。每当JavaScript程序创建一个字符串、数组或对象时,解释器就必须分配内存来存储这个实体。只要内存是这样动态分配的,最终都会被释放,以便重用;否则,JavaScript的解释器将消耗系统中所有可用的内存,导致系统崩溃。

  JavaScript的垃圾收集机制会间歇性地检查无用的变量和对象(垃圾),释放它们占用的空间。

  

四、可达性(Reachability)

  不同的编程语言采用不同的垃圾收集策略。比如C没有垃圾回收机制,所有的内存管理都要靠程序员自己的技能,导致C很难掌握的现状。JavaScript可达性用于管理内存。从字面上看,reachable就是可到达的意思,也就是程序可以以某种方式访问和使用的变量和对象。这些变量占用的内存无法释放。

  JavaScript指定了一组固有的可达值,并且该组中的值是固有可达的:

  当前正在执行的函数的上下文(包括函数中的局部变量、函数的参数等。);当前嵌套调用链中的其他函数,它们的局部变量和参数;全局变量;其他内部变量;上述变量称为,是可达性树的顶层节点。

  如果一个变量或对象被根变量直接或间接应用,它就被认为是可达的。

  换句话说,如果一个值可以通过根访问(例如,A.b.c.d.e),那么这个值就是可达的。

  

五、可达性举例

  

层次关联:

  让人={

  男生:{

  男孩1:{姓名:小明 },

  男孩2:{姓名:小军 },

  },

  女生:{

  女孩1:{姓名:小红 },

  女孩2:{姓名:花花 },

  }};上面的代码创建了一个对象,并将其分配给变量people。变量people包含两个对象,男孩和女孩,男孩和女孩分别包含两个子对象。这将创建一个具有三层引用关系的数据结构(不管底层类型数据如何),如下图所示:

  其中,people节点是自然可达的,因为它是一个全局变量。男孩和女孩节点是间接可达的,因为它们被全局变量直接引用。Boys1、boys2、girls1、girls2是全局变量间接应用的,可以通过people.boys.boys访问,所以也是可达变量。

  如果我们在上述代码后添加以下代码:

  people . girls . girls 2=null;people . girls . girls 1=people . boys . boys 2;然后,上面的参考层次图将变成如下:

  其中,girls1和girls2因为与grils节点断开连接而成为不可达节点,这意味着它们将被垃圾收集机制回收。

  如果此时,我们再次执行下面的代码:

  people . boys . boys 2=null;那么参考层次图将变成如下所示:

  此时,虽然男孩节点和男孩2节点断开连接,但是由于男孩2节点和女孩节点之间的引用关系,男孩2仍然是可达的,不会被垃圾收集机制收集。

  

相互关联:

  让人={

  男生:{

  男孩1:{姓名:小明 },

  男孩2:{姓名:小军 },

  },

  女生:{

  女孩1:{姓名:小红 },

  女孩2:{姓名:花花 },

  }};people.boys.boys2 .女朋友=people . girls . girls 1;

  //boys2引用girls1people.girls.girls1 .男友=people . boys . boys 2;//girls1引用boys2的上述代码,在boys2和girls1之间创建相互关联的关系。关系结构图如下:

  在这一点上,如果我们切断男孩和男孩之间的联系2:

  删除people . boys . boys 2;对象之间的关系图如下:

  显然,没有不可到达的节点。

  此时,如果我们断开男朋友关系:

  删除people . girls . girls 1;图表变成:

  此时,虽然boys2和girls1之间存在女朋友关系,但是boys2和不可达节点会被垃圾收集机制收集。

  

可达孤岛:

  让人={

  男生:{

  男孩1:{姓名:小明 },

  男孩2:{姓名:小军 },

  },

  女生:{

  女孩1:{姓名:小红 },

  女孩2:{姓名:花花 },

  }};删除people.boys删除people.girls上述代码形成的参考层次图如下:

  此时,尽管虚线框内的对象仍有相互引用,但这些对象也是不可达的,将被垃圾收集机制删除。这些节点已经脱离,变得不可达。

  

六、垃圾回收算法

  

引用计数

  所谓引用计数——计数,顾名思义就是每次引用一个对象就计数,加了就加1,删了就减1。如果引用号变为0,它将被认为是垃圾,因此可以删除该对象以回收内存。

  例如:

  let user={username:小明 };

  //对象被用户变量引用,计数1

  设user2=用户;

  //对象被新变量count 1引用

  user=null

  //变量不再引用对象,count -1

  user2=null

  //变量不再引用对象,odd -1

  //此时,对象的引用号为0,将被删除。虽然看起来引用计数法很合理,但实际上使用引用计数法的内存回收机制存在明显的漏洞。

  例如:

  let boy={ };

  let girl={ };

  男孩.女朋友=女孩;

  girl .男友=男孩;

  男孩=空;

  女生=null以上代码在男孩和女孩之间有交叉引用。如果通过计数删除了男孩和女孩中的引用,这两个对象将不会被回收。由于循环引用的存在,两个匿名对象的引用计数永远不会为零,从而导致内存泄漏。

  c中有一个智能指针(shared_ptr)的概念,程序员可以使用对象析构函数,通过智能指针释放引用计数。但是,在循环引用的情况下会发生内存泄漏。

  幸运的是,JavaScript采用了另一种更安全的策略,在更大程度上避免了内存泄漏的风险。

  

标记清除

  标记清除(mark and sweep)是JavaScript引擎采用的垃圾收集算法。其基本原理是,从开始,广度首先遍历变量之间的引用关系,用标记(优秀员工徽章)对遍历的变量进行标记,最后删除未标记的对象。

  该算法的基本过程如下:

  垃圾收集器找到所有,并为优秀员工颁发徽章(标志);然后遍历优秀员工,将优秀员工引用的对象标记为优秀员工。重复步骤2,直到没有新的优秀员工加入;未标记的对象将被删除。举个栗子:

  如果我们的程序中存在如下图所示的对象引用关系:

  我们可以清楚的看到,在整张图片的右侧有一个“可到达的岛屿”,从永远无法到达。但是收垃圾的没有我们上帝的视角。他们只是根据算法先把根节点标记为优秀员工。

  然后从优秀员工开始,找到优秀员工引用的所有节点,比如上图虚线框中的三个节点。然后将新发现的节点标记为优秀员工。

  重复搜索和标记的过程,直到所有能找到的节点都被成功标记。

  最后达到下图所示的效果:

  算法执行期结束后,右边的孤岛仍然没有标记,垃圾收集器任务无法到达这些节点,最后被清除。

  

七、性能优化

  垃圾收集是一项巨大的任务,尤其是在代码量非常大的情况下。频繁执行垃圾收集算法显然会拖累程序的执行。JavaScript算法在垃圾收集上做了很多优化,在保证垃圾收集正常执行的前提下,保证程序的高效执行。

  性能优化策略通常包括以下内容:

  

分代回收

   JavaScript程序在执行过程中会维护相当数量的变量,频繁扫描这些变量会造成明显的开销。然而,这些变量在生命周期中各有特点。比如局部变量会被频繁创建,快速使用然后丢弃,而全局变量会长时间占用内存。JavaScript对象是单独管理的。对于快速创建、使用和丢弃的局部变量,垃圾收集器会频繁扫描,以确保这些变量在失去作用后被快速清理。对于那些长时间占用内存的变量,减少检查的频率,从而节省一些开支。

  

增量收集

  增量思维在性能优化中非常常见,也可以用于垃圾收集。当变量数量非常大时,一次性遍历所有变量并发出优秀员工标志显然非常耗时,导致程序在执行过程中被卡住。所以引擎会把垃圾收集工作分成几个子任务,在程序执行的过程中逐步执行每个子任务,会造成一定的恢复延迟,但通常不会造成明显的程序卡顿。

  

空闲收集

   CPU即使在复杂的程序中也不是一直工作的。这主要是因为CPU工作速度非常快,外设IO往往要慢几个数量级。因此,在CPU空闲时安排垃圾收集策略是一种非常有效的性能优化手段,基本不会对程序本身产生不利影响。这种策略类似于系统的空闲时间升级,用户是不知道后台执行的。

  

八、总结

  本文的主要任务是简单结束垃圾收集机制、常用策略和优化手段,并不是让大家深入了解引擎的后台执行原理。

  通过这篇文章,你应该知道:

  垃圾收集是JavaScript的特点之一,在后台执行,我们不用担心。垃圾收集的策略是标记和清除垃圾,根据可达性理论筛选和清除垃圾。清晰的标记策略可以避免可达岛导致的内存泄漏【相关推荐:javascript视频教程,web前端】以上是JavaScript隐藏机制的垃圾收集知识总结的详细内容。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: