欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > 「JavaScript深入」谈谈 JS 的垃圾回收机制

「JavaScript深入」谈谈 JS 的垃圾回收机制

2024/10/25 6:33:44 来源:https://blog.csdn.net/XH_jing/article/details/142496628  浏览:    关键词:「JavaScript深入」谈谈 JS 的垃圾回收机制

JavaScript深入 — 垃圾回收

    • 内存管理
    • 可达性
    • 内存回收机制
      • 标记清除
      • 标记整理
      • 引用计数
    • V8对于垃圾回收机制的优化
      • 分代式垃圾回收
      • 新生代内存回收
      • 老生代内存回收


在这里插入图片描述

内存管理

在JavaScript编程中,内存管理大概分成三个步骤,也是内存的声明周期:

  • 分配你所需系统内存的空间
  • 使用分配到的内存进行读写操作
  • 不需要使用内存时,将空间进行释放和归还

与其它手动管理内存的语言不一样的是,在JavaScript中,当我们创建变量时,系统会给对象进行自动分配对应的内存空间以及闲置资源回收

对于基础数据类型和引用数据类型,符合直觉的内存分配机制如下:

  • 简单数据类型内存保存在固定的栈空间中,可直接通过值进行访问
  • 引用数据类型的值大小不固定,其引用地址保存在栈空间、引用所指向的值保存在堆空间中,需要通过引用进行访问

栈内存中的基本数据类型,可以直接通过操作系统进行处理,而堆内存中的引用数据类型的值大小不确定,因此需要JS的引擎通过垃圾回收机制进行处理。


可达性

JavaScript中内存管理的主要概念是可达性

简单来说,“可达性”值就是哪些以某种方式可访问或可用的值,它们被保证存储在内存中

🌰 一个简单的例子

// user 具有对象的引用
let user = {name: "John"
}

在这里插入图片描述

👆 这里箭头表示一个对象引用,全局变量 user 引用对象 {name:"John"} ,如果user的值被覆盖,则引用丢失

user = null

在这里插入图片描述

👆 现在对象 {name:"John"} 变成了不可达的状态,没有办法访问它,垃圾回收器将丢弃其数据并释放内存


内存回收机制

垃圾回收算法:垃圾收集器按照固定的事件间隔,周期性的寻找哪些不再使用的变量,然后将其清除或释放内存。

在浏览器的发展历史上有两种解决策略:

  • 标记清除
  • 引用计数

标记清除

首先它会遍历堆内存上所有的对象,分别给它们打上标记,然后在代码执行过程结束之后,对所使用过的变量取消标记。在清除阶段再把具有标记的内存对象进行整体清除,从而释放内存空间。

在这里插入图片描述

大致过程如下:

  • 垃圾收集器在运行时会给内存中的所有变量都加上一个标记
  • 然后从各个根对象开始遍历,把还在被上下文变量引用的变量标记去掉标记
  • 清理所有带有标记的变量,销毁并回收它们所占用的内存空间
  • 最后垃圾回收程序做一次内存清理

使用标记清除策略最重要的优点在于简单,无非是标记和不标记的差异。通过标记清除后,剩余的对象内存位置是不变的,也导致空闲内存空间是不连续的(如上图),这就造成了出现内存碎片的问题。对于标记清除产生的内存碎片,还是需要通过标记整理策略进行解决。

  • 优点:简单
  • 缺点:内存碎片化、分配速度慢

👇 图示标记清除的过程

在这里插入图片描述

👇 第一步标记根

在这里插入图片描述

👇 然后标记他们的引用以及子孙代的引用

在这里插入图片描述

👇 现在进程中不能访问的对象被认为是不可访问的,将被删除

在这里插入图片描述

标记整理

标记结束后,标记整理算法会将不需要清理的对象向内存的一端移动,最后清理掉边界的内存

在这里插入图片描述

引用计数

引用计数是一种不常见的垃圾回收策略,思路是对每个值都记录其的引用次数,具体如下

  • 当变量进行声明并赋值后,值的引用数为1
  • 当同一个值被赋值给另一个变量时,引用书+1
  • 当保存该值引用的变量被其它值覆盖时,引用数-1
  • 当该值的引用数为0时,表示无法再访问该值了,此时就可以放心的将其清除并回收内存

💥 但有循环引用问题如下

function problem(){let objectA = new Object();let objectB = new Object();objectA.somOtherObject = objectB;objectB.anotherObject = objectA;
}

如上例子中,objectAobjectB 通过各自的属性相互引用,意味着它们的引用数都是2,在标记清理策略下,objectAobjectB 虽然互相引用了对方,但没有其他引用指向这两个对象,它们将会被标记为不可达,并最终会被垃圾回收器清除。


V8对于垃圾回收机制的优化

大多数浏览器都是基于标记清除算法,V8对其进行了一些优化加工处理。

  • 分代式垃圾回收
  • 新生代内存回收
  • 老生代内存回收

分代式垃圾回收

V8的垃圾回收策略主要基于分代式垃圾回收机制,V8中将堆内存分为新生代和老生代两区域,采用不同的策略管理垃圾回收

  • 新生代的对象为存活时间较短的对象,简单来说就是新产生的对象,通常只支持 1~8M 的容量
  • 老生代的对象为存活事件较长或常驻内存的对象,简单来说就是经历过新生代垃圾回收后还存活下来的对象,容量通常比较大。

在这里插入图片描述

新生代内存回收

在64位操作系统下分配为32MB,因为新生代中的变量存活时间短,不太容易产生太大的内存压力

通常通过Scavenge的算法进行垃圾回收,就是将新生代内存进行一份为二,正在被使用的内存空间称为使用区,而限制状态的内存空间称为空闲区

在这里插入图片描述

  • 新加入的对象都会存放在使用区,当使用区快写满时就进行一次垃圾清理操作。
  • 在开始进行垃圾回收时,新生代回收器会对使用区内的对象进行标记
  • 标记完成后,需要对使用区内的活动对象拷贝到空闲区进行排序
  • 而后进入垃圾清理阶段,将非活动对象占用的内存空间进行清理
  • 最后对使用区和空闲区进行交换,使用区->空闲区,空闲区->使用区

老生代内存回收

对内存空间较大的不适合上述Scavenge算法,此时应使用标记清除标记整理的策略进行老生代内存中的垃圾回收

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com