1写在前面本文主要围绕JS引擎相关知识,深入理解底层运行逻辑,对于日常开发维护高性能Javascript代码,排查代码性能问题有很大帮助.关于JS引擎底层的垃圾回收机制,内存泄露的问题,后期可以通过人工预防和优化来实现对JS内存管理和内存溢出的处理。那么我们需要考虑几个问题:什么是垃圾回收(GC)?垃圾是怎么产生的?为什么需要垃圾收集?Javascript内存是如何管理的?Chrome浏览器是如何进行垃圾回收的?2内存管理在Javascript编程中,内存管理大致分为三个步骤,这也是内存的生命周期:为系统内存分配你需要的空间使用分配的内存进行读写操作当不需要内存时,释放空间并归还内存Javascript的生命周期不同于其他手动管理内存的语言。在Javascript中,当我们创建一个变量时,系统会自动为该对象分配相应的内存空间并回收空闲资源,即不需要我们手动分配。但是,也正是因为垃圾回收机制,让开发者产生了不需要关心内存管理的错误感觉。constname="yichuan";//为字符串分配栈内存constage=18;//为值分配栈内存//为对象和包含的值分配堆内存constuser={name:"onechuan",age:19}//为数组和包含的值分配堆内存constarr=["yichuan","onechuan",18];//为函数对象functionsum(x,y){returnx+y;}分配堆内存上一篇《Javascript的数据类型知多少》,我们知道了基本数据类型和引用数据类型的分配机制,即:简单数据类型内存存放在一个固定的栈空间,可以直接按值访问。引用数据类型的值大小不固定,它的引用地址存放在栈中空间和引用指向的值存放在堆空间中,栈内存中的基本数据类型需要通过引用访问,可以直接通过操作系统处理,而引用数据类型在堆内存中的值大小是不确定的,所以需要JS引擎通过垃圾回收机制来处理。3内存回收机制(GC)Javascript的V8引擎对内存的使用是有限制的,因此内存大小会根据不同的操作系统而有所不同。V8引擎最初是作为浏览器引擎设计的,并没有考虑占用过多的内存空间。随着web技术工程的发展,它占用的内存空间越来越大。并且因为受限于v8的回收机制,这会导致js执行的线程被挂起,从而影响当前正在执行的页面应用的性能。垃圾收集算法:垃圾收集器以固定的时间间隔周期性地寻找不再使用的变量,然后清除或释放内存。但是垃圾回收算法是一个不完美的解决方案,因为某一块内存是否仍然可用是一个不可预测的问题,也就是说不能仅仅依靠算法来解决。而为什么不找出无用的内存并实时释放呢?其实很简单,实时性开销太大。我们知道垃圾是如何产生的,那么我们应该如何清除呢?在浏览器发展史上,有两种解决方案:markclear引用计数markclearmarkclear分为:markphase和clearphase。首先会遍历堆内存上的所有对象,分别进行标记,代码执行过程结束后,再对使用过的变量取消标记。在清除阶段,被标记的内存对象被整体清除,释放内存空间。整个标记清除算法的大致过程是这样的:垃圾收集器会在运行时为内存中的所有变量加上一个标记,然后从每个根对象开始遍历,去除仍然被引用的变量的标记通过上下文变量。带有标签机的变量,销毁和回收它们所占用的内存空间,最后由垃圾回收器做一次内存清理。使用mark-clear策略最重要的优点就是简单,无非就是标记和不标记的区别。标记清除后,剩余对象内存位置不变,空闲内存空间也不连续,造成内存碎片问题。当内存碎片过多时,如果要存储一个新的对象需要占用较大的内存空间,就会有影响。对于标记和清除产生的内存碎片,还是需要通过标记和清除策略来解决。简而言之:优点:简单缺点:内存碎片,分配速度慢markcollat??ionmarkclearingstrategycollect后,老年代内存中会产生很多内存碎片,如果内存碎片不清理,会影响存储。Mark-Compact算法可以有效解决标记去除的两个缺点。它的标记阶段与标记清除算法没有区别,只是标记结束后,标记清除算法会将存活对象(即不需要清除的对象)移动到内存的一端,最后清理边界内存。引用计数引用计数是一种不常见的垃圾回收策略,其思想是记录每个值被引用的次数。具体来说:当一个变量被声明并赋值时,该值的引用数为1。当相同的值被赋给另一个变量时,引用数+1,当持有该值引用的变量被其他值覆盖时,引用号-1当该值的引用号为0时,表示该值不能再被访问,此时可以安全的清除它,回收内存。leta=newObject()//这个对象的引用计数为1(a引用)letb=a//这个对象的引用计数为2(a,b引用)a=null//这个对象的引用计数为1(bReference)b=null//这个对象的引用计数为0(无引用)...//GC回收这个对象的回收策略看似很方便,但是在循环引用的时候,会有问题,这将导致大量内存未被释放。当函数结束时,这两个对象不在作用域中,A和B将被清除为非活动对象。相比之下,引用计数不会释放,会占用大量无用的内存。这也是后来放弃引用计数而采用标记清除的原因之一。4V8对垃圾回收机制的优化大多数浏览器都是基于标记清除算法,区别仅在于运行垃圾回收的频率。V8对其进行了一些优化处理,接下来我们主要看一下V8中垃圾回收机制的优化。分代垃圾回收V8的垃圾回收策略主要基于分代垃圾回收机制。在V8中,堆内存分为新生代和老年代两个区域,使用不同的垃圾收集器来管理垃圾收集。新生代的对象是生存时间较短的对象。简单的说就是新生成的对象,一般只支持1-8M的容量。老年代的对象是具有较长生存事件或常驻内存的对象。简单的说,就是在新生代中从垃圾回收中幸存下来的对象,其容量通常比较大。V8整个堆内存的大小等于新生代内存加上老年代内存。对于新旧内存区的垃圾回收,V8使用了两个垃圾回收器来控制。新生代和老年代新生代的内存回收在64位操作系统下分配为32MB。因为新生代中的变量存活时间短,不容易产生太大的内存压力,所以不够大也无可厚非。对于新生代内存的回收,通常通过Scavenge算法进行垃圾回收,即将新生代内存一分为二。正在使用的内存空间称为usedarea,处于受限状态的内存空间称为freearea。新生代内存回收的原理是:新加入的对象会存放在使用区,当使用区快满时会进行一次垃圾清理操作。在垃圾收集开始时,新生代收集器会标记使用区域中的对象。标记完成后,需要将使用区的活动对象复制到空闲区进行排序,然后进入垃圾清理阶段,清除非活动对象占用的对象。清理内存空间,最后将已用区域与空闲区域进行交换,使用区域->空闲区域,空闲区域->使用区域如果新生代中的变量回收后仍然存在,则将其放入老年代内存中.只要被Scavenge算法回收过一次,就可以提升为老年代内存中的对象。老年代内存回收当然Scavenge算法也有它的适用场景,Scavenge算法不适合大内存空间。这时候就应该采用Mark-Sweep(标记清除)和Mark-Compact(标记压缩)的策略对老年代内存进行垃圾回收。首先是标记阶段,从一组根元素开始,递归遍历这组根元素,遍历过程中可以到达的元素称为活动对象,不能到达的元素可以判断为非活动对象。在清理阶段,老年代垃圾回收器会直接清理不活跃的对象,也就是数据。同样的标记清除策略会产生内存碎片,所以也需要标记清除策略进行优化。5内存泄漏及优化内存泄漏是指在JS中分配了内存地址的对象,由于长期未释放或无法清除,导致长期内存占用,浪费内存资源,最终导致运行缓慢运行应用程序的响应速度和最终故障。代码中创建对象和变量时,会占用内存,但是JS基于自身的内存回收机制,可以判断哪些变量不再需要,并清除掉。但是,当你的代码出现了逻辑上的缺陷,你以为你不需要它了,但是这个引用仍然存在于程序中,导致程序运行后占用的内存空间没有被正常回收。运行时间越长,占用内存越多,随之而来的问题是:性能差,延迟高,经常死机。内存泄漏的常见原因有:缓存过多。及时清理过多的缓存。滥用闭包。尽量避免使用大量闭包。太多的计时器或回调。当不再需要与节点或数据关联的计时器时,可以清除DOM节点对象,不再需要整个回调函数。但是定时器回调函数还是没有被回收(定时器停止时会被回收)。当不需要setTimeout或setInterval时,定时器没有被清除,定时器的粘贴函数及其内部因变量不能被回收,会造成内存泄漏。解决方法:定时器完成工作后需要手动清零定时器。无效的DOM引用过多。DOM被删除了,但是节点的引用还在,导致GC无法回收它占用的内存。解决方案:将对已删除DOM节点的引用设置为null。滥用全局变量。全局变量根据定义不是垃圾回收,因此需要特别注意临时存储和处理大量信息的全局变量。如果您必须使用全局变量来存储数据,请确保将其分配为null或在完成后重新分配。解决方法:使用严格模式。从外到内执行appendChild。这时候即使调用了removeChild,也无法释放内存。解决办法:appendChild由内向外。重复改写相同的数据会造成大量的内存占用,但在IE浏览器关闭后就会释放。注意程序逻辑,避免写“死循环”之类的代码。DOM对象和JS对象相互引用。关于内存泄漏,如果你想更好的排查问题,提前避免问题,最好的解决办法是熟练使用Chrome的内存分析工具,分析定位Chrome帮你分析保留的内存快照,查看连续占用内存的Objects很多内存。6参考文章《「硬核JS」你真的了解垃圾回收机制吗》?《Javascript高级程序设计》7写在下面的文章中,这篇文章讲的是JS的内存管理机制和v8的垃圾回收机制。最后,我们还分析了一些日常编码中经常遇到的内存泄漏问题。根据不同的原因给出相应的解决方案。【编辑推荐】HarmonyOS官方战略合作共建——HarmonyOS技术社区Windows11出现新BUG,搜索邮件也会卡住越来越完善,微软将进一步优化飞行模式14款新浏览器攻击出现,影响谷歌,微软、苹果和火狐浏览器微软发布OneDriveARM64预览版,全面兼容Windows11/10ARM和M1Mac
