V8引擎的垃圾回收机制

161 阅读4分钟

前言

在之前的文章中写到了垃圾回收机制,作为目前最流行的JavaScript引擎,V8引擎从出现的那一刻起就引起了人们的广泛关注。而V8引擎为了追求极致的性能和更好的用户体验,又做了一系列的事情,那V8引擎的垃圾回收机制是怎样的呢?接下来我们一起来看一下。

V8引擎的内存限制

V8引擎是Google利用C++编写的开源高性能JavaScprit和WebAssembly引擎,它用于Chrome和Node.js等,可以独立运行,也可以嵌入到任何C++的应用程序中。

V8会编译/执行 JavaScript 代码,管理内存,负责垃圾回收,与宿主语言的交互等。

V8引擎是自动实现了垃圾回收管理的,不需要我们在coding的时候操心,但是呢V8引擎中的内存使用也并不是无限制的。那么为什么要有这种限制呢?
主要是因为:

  • JavaScript的单线程机制:垃圾回收的时候就会导致主线程的暂停
  • 垃圾回收机制:垃圾回收本身也是一件非常耗时的操作,如果长时间的垃圾回收就会导致浏览器长时间无法得到响应,用户就会一直处于等待中。
    V8引擎为了减少对性能造成的影响,就直接限制了堆内存的大小。

V8引擎的内存分配

V8使用堆来管理JavaScript使用的数据、以及生成的代码、哈希表等。为了更方便地实现垃圾回收,同很多虚拟机一样,V8将堆分成三个部分:新生代老生代大对象

新生代:为新创建的对象分配内存空间,经常需要进行垃圾回收。为方便新生代中的内容回收,可再将新生代分为两半,一半用来分配,另一半在回收时负责将之前还需要保留的对象复制过来。

老生代:根据需要将生命周期较长的对象、指针、代码等数据保存起来,较少地进行垃圾回收。

大对象:为那些需要使用较多内存对象分配内存,当然同样可能包含数据和代码等分配的内存,一个页面只分配一个对象。

新生代垃圾回收实现

新生代是将内存分为两半,一半是用来分配,一半是用来复制要保留的对象。新生代的回收是采用的复制算法+标记管理方法。 image.png 在进行一次垃圾回收的时候,会看一下From内存中有没有需要保留的对象,如果有,将要保留的对象复制到To内存中,然后将From内存中的所有对象进行清除,最后将From内存与To内存进行交换,即From内存变成To内存,To内存变成From内存

老生代垃圾回收实现

在新生代中我们会发现有将From内存中的对象复制到To内存中这一步骤,那是不是所有的对象都能进行这一步呢,当然不是。不然这样就不会有老生代的存在必要了。当有以下两个条件的时候,就不能进行这一步:

  • 对象是否已经经历过一次交换,如果进行过了就要移动到老生代中
  • To内存的空间占比是否已经超过了25%,如果超过了,也要移动到老生代中 在老生代中采用的垃圾回收算法是标记清除标记整理,这在谈谈垃圾回收机制有说明过,不清楚的可以去看一下。

为了减少垃圾回收带来的停顿,V8引擎又引入了增量标记的概念,即将原本需要一次性遍历堆内存的操作改为增量标记的方式,先标记堆内存中的一部分对象,然后暂停,将执行权重新交给JS主线程,待主线程任务执行完毕后再从原来暂停标记的地方继续标记,直到标记完整个堆内存。

总结

本文主要从新生代和老生代两方面讲述了V8引擎的垃圾回收策略,根据这些策略呢,我们应该知道在平常工作中,可以通过以下几点避免内存泄漏。

  • 尽可能少的使用全局变量
  • 手动清除定时器
  • 少用闭包