V8引擎的垃圾回收机制

垃圾回收是指在 JavaScript 运行时自动回收不再使用的内存空间的一种机制。JavaScript 具有自动垃圾回收机制,会定期对那些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其占用的内存。

一、垃圾回收的必要性

  • 内存泄漏:当程序中动态分配的堆内存由于某种原因未释放或无法释放时,就会引发内存泄漏。在JavaScript中,由于变量是自动分配的,如果不再需要的变量没有被正确地标记为可回收,就会导致内存泄漏。

  • 内存管理:传统的编程语言需要开发者手动管理内存的申请、使用和释放。而JavaScript则通过垃圾回收机制来自动管理内存,减轻了开发者的负担。

二、常见垃圾回收策略

  1. 引用计数法:

  • 原理:记录每个对象的引用数,当没有引用时,视为不需要并被回收。这种方法简单高效,但处理循环引用时有问题。

  • 限制:无法解决对象间的相互引用问题,即当两个对象互相引用,但不被其他活跃对象引用时,它们无法被正确回收。

  1. 标记清除法:

  • 原理:分为标记和清除两个阶段。先从根结点遍历活跃对象并打上标记,然后回收未标记的对象。能处理循环引用的对象。

  • 优点:能处理对象间的相互引用,防止内存泄漏。

  • 限制:可能导致内存碎片问题。为解决这个问题,可使用更先进的垃圾回收算法。同时,结合其他优化手段提高效率。

三、V8引擎的垃圾回收机制

V8引擎是Google开发的开源JavaScript引擎,广泛应用于Chrome浏览器和Node.js环境。它的垃圾回收机制主要基于标记清除法,并有很多优化和改进。以下是V8引擎垃圾回收的简单流程:

  1. 初始阶段:V8引擎启动后,会分配堆内存并初始化垃圾回收器。

  2. 标记清除:从全局变量开始,标记所有活跃对象。未标记的对象会被回收。

  3. 分代回收:V8将内存分为新生代和老生代。新生代区域垃圾回收频繁,使用Scavenge算法;老生代区域垃圾回收较少,但规模较大,采用标记清除法并结合其他优化手段。

  4. 处理内存碎片:通过压缩指针等技术减少内存碎片。

  5. 并发执行和性能优化:V8的垃圾回收机制并发执行,以减少对性能的影响,并持续改进和优化算法,提高效率。

2021031416310447.png

四、内存泄漏问题

内存泄漏指的是内存中已经不再使用的对象仍然被占用,不能被垃圾回收机制释放,从而导致内存空间的浪费。以下是一些常见的情况,可能会导致内存泄漏:

  1. 循环引用:当两个或多个对象相互引用时,它们的引用计数永远不会变成 0,导致内存泄漏。

  2. 全局变量:全局变量会一直存在于内存中,直到页面关闭,因此应该尽量避免使用全局变量。

  3. 未清理的定时器或回调函数:如果定时器或回调函数没有被清理,它们会一直存在于内存中,导致内存泄漏。

  4. 闭包:闭包会导致内存泄漏,因为闭包会保留对外部函数的引用,使得外部函数的变量无法被垃圾回收。

  5. DOM 引用:如果一个 DOM 元素被移除了,但是仍然被 JavaScript 引用着,那么这个 DOM 元素就无法被垃圾回收。

  6. 事件监听器:如果一个元素被添加了事件监听器,但是没有被正确地移除,那么这个元素就无法被垃圾回收。

  7. 内存泄漏的第三方库:一些第三方库可能会导致内存泄漏,因此应该尽量避免使用不可靠的第三方库。

五、总结

GC算法采用了分代式垃圾回收机制,V8将内存堆分为新生代和老生代,新生代中的对象一般存活时间较短,使用ScavengeGC算法,在新生代空间中内存空间被分为FROM空间和TO空间,两个空间中必定有一个是使用的一个是空闲的,新分配的对象会被放入from空间中,当from占满时,新生代GC就会启动,算法会检查from空间中存活的对象,并复制到to空间中,如果有失活的对象就销毁,当复制完成后将两个空间互换,这样GC就结束了,老生代算法,老生代对象一般存活时间较长且数量较多,使用了两个算法,标记清除算法和标记压缩算法,什么情况对象会从新生代到老生代?1.新生代的对象经历了一次ScavengeGC算法。2. to空间的对象占比大小超过25%,为了不影响内存分配,对象就会被移到老生代空间中,在这个阶段中会遍历所有对象,然后标记活的对象,标记完成后销毁所有没有被标记的对象,标记一次大型堆内存时,可能需要几百毫秒,2011采用了增量标记,2018采用了并发标记,清除对象后会造成堆内存出现碎片的情况,当碎片超过一定限制后会启动压缩算法。

拓展:weakMap weakSetMap Set 有什么区别?

  1. Map 和 WeakMap:

    • Map:是一个键值对的集合,可以存储任何类型的键值对,并允许重复键的存在。它提供了 O(1) 时间复杂度的插入、删除和查找操作。

    • WeakMap:与 Map 类似,但有一个重要的区别:其键必须是弱引用的对象。当键对象在其他地方没有被引用时,它不会在 WeakMap 中占据任何内存空间。这意味着 WeakMap 中的条目不会阻止垃圾回收机制回收键对象。因此,WeakMap 主要用于存储与特定对象紧密相关的数据,而不阻止这些对象被垃圾回收。

  2. Set 和 WeakSet:

    • Set:是一个值的集合,不允许重复值的存在。它提供了 O(1) 时间复杂度的添加、删除和查找操作。Set 可以存储任何类型的值。

    • WeakSet:与 Set 类似,但它只允许存储弱引用的对象作为成员。与 WeakMap 类似,WeakSet 中的对象不会阻止垃圾回收机制回收它们。WeakSet 主要用于跟踪对象的所有权或表示对象的存在而不干扰其垃圾回收。

总结:MapSet 是正常的集合数据结构,它们存储的数据可以独立存在,不会因为外部引用而阻止垃圾回收。而 WeakMapWeakSet 则专注于与特定对象保持弱引用关系的数据存储,不会阻止这些对象的垃圾回收。这是它们之间的主要区别。