返回

垃圾回收之旅:V8垃圾回收器是如何工作的?

前端

在JavaScript的世界里,垃圾回收(Garbage Collection,简称GC)是一项必不可少的技术,它能够自动回收不再被程序引用的内存空间,从而防止内存泄漏和提高程序的性能。在V8引擎中,有两个垃圾回收器共同承担着这重要任务,它们分别是主垃圾回收器和副垃圾回收器。

主垃圾回收器:标记清除,高效可靠

主垃圾回收器采用标记清除算法来进行垃圾回收。它的工作流程主要包括两个步骤:

  1. 标记阶段: 主垃圾回收器首先会从根节点开始,递归地标记所有可达的对象。所谓根节点,是指那些由程序直接引用的对象,例如全局变量、函数参数和局部变量等。在标记阶段,主垃圾回收器会将这些可达对象标记为“存活”,而那些不可达的对象则会被标记为“死亡”。

  2. 清除阶段: 在标记阶段完成后,主垃圾回收器就会开始清除那些被标记为“死亡”的对象所占用的内存空间。这个过程非常简单,主垃圾回收器会简单地将这些对象从内存中删除,并释放其所占用的内存空间。

标记清除算法虽然简单高效,但它也有一个缺点:它会导致内存碎片。当主垃圾回收器清除那些“死亡”对象后,内存中就会出现一些空闲的内存块。这些空闲的内存块大小不一,并且分布在内存的各个角落。当程序需要分配新的内存空间时,主垃圾回收器必须在这些空闲的内存块中找到一块足够大的连续空间。如果找不到,主垃圾回收器就需要对内存进行整理,将这些空闲的内存块合并成一块连续的空间。这个过程称为内存压缩。

副垃圾回收器:标记压缩,快速灵敏

为了解决主垃圾回收器导致的内存碎片问题,V8引擎引入了副垃圾回收器。副垃圾回收器采用标记压缩算法来进行垃圾回收。它的工作流程与主垃圾回收器类似,但它在标记阶段结束后不会立即清除那些被标记为“死亡”的对象,而是会将这些对象移动到内存的另一块区域。这块区域称为“临时区域”。在移动完成后,副垃圾回收器就会对临时区域进行压缩,将那些“死亡”对象所占用的内存空间释放出来。

标记压缩算法可以有效地解决内存碎片问题,但它也有一个缺点:它比标记清除算法更耗时。因此,V8引擎会在适当的时候使用副垃圾回收器来进行垃圾回收,以避免对程序的性能造成太大的影响。

增量标记和并发标记:优化垃圾回收的利器

为了进一步优化垃圾回收的性能,V8引擎还引入了增量标记和并发标记技术。

增量标记是指主垃圾回收器在进行标记阶段时,并不是一次性地标记所有可达的对象,而是分批进行。这样可以减少垃圾回收对程序执行的影响。

并发标记是指主垃圾回收器在进行标记阶段时,可以与程序并行执行。这可以进一步减少垃圾回收对程序执行的影响。

写屏障、指针修复和WeakMap:垃圾回收的辅助技术

除了上述垃圾回收算法和技术之外,V8引擎还引入了一些辅助技术来帮助垃圾回收器进行工作。这些辅助技术包括写屏障、指针修复和WeakMap。

写屏障是一种技术,它可以帮助主垃圾回收器和副垃圾回收器识别那些被修改过(重新赋值)的对象。这样,垃圾回收器就可以只标记和清除那些被修改过(重新赋值)的对象,而不是所有的对象。

指针修复是一种技术,它可以帮助主垃圾回收器和副垃圾回收器修复那些指向被清除对象的指针。这样,程序就可以继续正常运行,而不会因为指针指向了被清除的对象而发生错误。

WeakMap是一种特殊的Map对象,它可以帮助程序跟踪那些不再被引用的对象。当一个对象被WeakMap所跟踪时,主垃圾回收器和副垃圾回收器就会知道这个对象不再被引用了,并将其标记为“死亡”。

总结

V8引擎的垃圾回收器是一个复杂且高效的系统。它采用多种算法和技术来回收不再被程序引用的内存空间,从而防止内存泄漏和提高程序的性能。在本文中,我们介绍了V8引擎中两个垃圾回收器的工作原理,并探讨了它们的特性和差异。我们还介绍了增量标记、并发标记、写屏障、指针修复和WeakMap等相关技术。通过了解这些技术,我们可以更好地理解JavaScript的垃圾回收机制,并为编写更健壮、更高效的JavaScript代码打下坚实的基础。